转载地址:http://blog.csdn.net/it_man/article/details/26217143
背景:
测试部门做压力测试, 结果没压多久,就出现OutOfMemory.
原因查找,通过监控工具,发现StandardSession(org.apache.catalina.session.StandardSession)对象不断增长,毫无疑问,肯定是在不断创建Session对象.
备注:一般做压力测试,每次请求都不会指定JESSESIONID值,导致Web容器认为每次请求都是新的请求,于是创建Session对象.
同事负责代码Review,发现应用没有任何一个地方存放Session内容.困惑之…
先看Servlet协议描述:
getSession(boolean create)
当create变量为true时,如果当前session存在,返回session;如果当前session不存在,创建一个新的Session并且返回;
getSession()
默认create变量为true,如果当前session存在,返回session;如果当前session不存在,创建一个新的Session并且返回;
getSession()和getSession(true)是等价的;
调用Request中的getSession()方法的源码分析如下:
注意事项:
(1)创建新的session时会,默认创建与对应的cookie,这个cookie存储了session的标识sessionId;
org.apache.catalina.connector.Request
类中的部分代码:
/**
* Return the session associated with this Request, creating one
* if necessary.
*/
@Override
public HttpSession getSession() {
//true代表session存在直接返回,不存在创建一个新的session
//org.apache.catalina.Session
//org.apache.catalina.session.StandardSession
Session session = doGetSession(true);
if (session == null) {
return null;
}
//org.apache.catalina.session.StandardSession的getSession方法
return session.getSession();
}
/**
* Return the session associated with this Request, creating one
* if necessary and requested.
*
* @param create Create a new session if one does not exist
*/
@Override
public HttpSession getSession(boolean create) {
Session session = doGetSession(create);
if (session == null) {
return null;
}
return session.getSession();
}
protected Session doGetSession(boolean create) {
// There cannot be a session if no context has been assigned yet
// context
Context context = getContext();
if (context == null) {
return (null);
}
// Return the current session if it exists and is valid
if ((session != null) && !session.isValid()) {
session = null;
}
if (session != null) {
return (session);
}
// Return the requested session if it exists and is valid
Manager manager = context.getManager();
if (manager == null) {
return null; // Sessions are not supported
}
if (requestedSessionId != null) {
try {
session = manager.findSession(requestedSessionId);
} catch (IOException e) {
session = null;
}
if ((session != null) && !session.isValid()) {
session = null;
}
if (session != null) {
session.access();
return (session);
}
}
// Create a new session if requested and the response is not committed
if (!create) {
return (null);
}
if ((response != null) &&
context.getServletContext().getEffectiveSessionTrackingModes().
contains(SessionTrackingMode.COOKIE) &&
response.getResponse().isCommitted()) {
throw new IllegalStateException
(sm.getString("coyoteRequest.sessionCreateCommitted"));
}
// Re-use session IDs provided by the client in very limited
// circumstances.
String sessionId = getRequestedSessionId();
if (requestedSessionSSL) {
// If the session ID has been obtained from the SSL handshake then
// use it.
} else if (("/".equals(context.getSessionCookiePath())
&& isRequestedSessionIdFromCookie())) {
/* This is the common(ish) use case: using the same session ID with
* multiple web applications on the same host. Typically this is
* used by Portlet implementations. It only works if sessions are
* tracked via cookies. The cookie must have a path of "/" else it
* won't be provided for requests to all web applications.
*
* Any session ID provided by the client should be for a session
* that already exists somewhere on the host. Check if the context
* is configured for this to be confirmed.
*/
if (context.getValidateClientProvidedNewSessionId()) {
boolean found = false;
for (Container container : getHost().findChildren()) {
Manager m = ((Context) container).getManager();
if (m != null) {
try {
if (m.findSession(sessionId) != null) {
found = true;
break;
}
} catch (IOException e) {
// Ignore. Problems with this manager will be
// handled elsewhere.
}
}
}
if (!found) {
sessionId = null;
}
}
} else {
sessionId = null;
}
session = manager.createSession(sessionId);
// Creating a new session cookie based on that session
if ((session != null) && (getContext() != null)
&& getContext().getServletContext().
getEffectiveSessionTrackingModes().contains(
SessionTrackingMode.COOKIE)) {
Cookie cookie =
ApplicationSessionCookieConfig.createSessionCookie(
context, session.getIdInternal(), isSecure());
response.addSessionCookieInternal(cookie);
}
if (session == null) {
return null;
}
session.access();
return session;
}
org.apache.catalina.session.StandardSession
类中的部分代码:
/**
* Return the <code>HttpSession</code> for which this object
* is the facade.
*/
@Override
public HttpSession getSession() {
if (facade == null){
if (SecurityUtil.isPackageProtectionEnabled()){
final StandardSession fsession = this;
facade = AccessController.doPrivileged(
new PrivilegedAction<StandardSessionFacade>(){
@Override
public StandardSessionFacade run(){
return new StandardSessionFacade(fsession);
}
});
} else {
facade = new StandardSessionFacade(this);
}
}
return (facade);
}
org.apache.catalina.session.StandardSessionFacade
类是StandardSession包装类:
package org.apache.catalina.session;
import java.util.Enumeration;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
/**
* Facade for the StandardSession object.
*
* @author Remy Maucherat
*/
public class StandardSessionFacade
implements HttpSession {
// ----------------------------------------------------------- Constructors
/**
* Construct a new session facade.
*
* @param session The session instance to wrap
*/
public StandardSessionFacade(StandardSession session) {
super();
this.session = session;
}
/**
* Construct a new session facade.
*/
public StandardSessionFacade(HttpSession session) {
super();
this.session = session;
}
// ----------------------------------------------------- Instance Variables
/**
* Wrapped session object.
*/
private HttpSession session = null;
............................
}