应用部署后,内存会随着使用时长边长,内存逐渐被吃掉,直到最后爬不动。开始研究一下吧......
首先,dump下来java应用看看到底哪里占用了那么多内存:
看到了吧,这个DefaulWebSessionManager居然占用了84%的内存,仔细看一下,主要是MemorySessionDAO中的ConcurrentHashMap中,这个map中为什么这么多的SimpleSession呢:
相关的继承关系如下:
DefaultWebSessionManager --> DefaultSessionManager --> AbstractValidatingSessionManager
其中sessionDAO在DefaultSessionManager中声明:
在这个MemorySessionDAO中维护了ConcurrentHashMap。其维护的机制如下:
public AbstractValidatingSessionManager() { this.sessionValidationSchedulerEnabled = true; this.sessionValidationInterval = DEFAULT_SESSION_VALIDATION_INTERVAL; }
在AbstractValidatingSessionManager中一个关键的地方是设定了一个scheduler专门负责处理过期的session。
protected SessionValidationScheduler createSessionValidationScheduler() { ExecutorServiceSessionValidationScheduler scheduler; if (log.isDebugEnabled()) { log.debug("No sessionValidationScheduler set. Attempting to create default instance."); } scheduler = new ExecutorServiceSessionValidationScheduler(this); scheduler.setInterval(getSessionValidationInterval()); if (log.isTraceEnabled()) { log.trace("Created default SessionValidationScheduler instance of type [" + scheduler.getClass().getName() + "]."); } return scheduler; }
看到了吧,就是这个ExecutorServiceSessionValidationScheduler。看看这个scheduler里面做了什么:
public void run() { if (log.isDebugEnabled()) { log.debug("Executing session validation..."); } long startTime = System.currentTimeMillis(); this.sessionManager.validateSessions(); long stopTime = System.currentTimeMillis(); if (log.isDebugEnabled()) { log.debug("Session validation completed successfully in " + (stopTime - startTime) + " milliseconds."); } }
就是validationSessions这个方法,处理了过期的session,并把它从那个hashMap中删除掉。实际调用了AbstractValidatingSessionManager.validateSessions这个方法。
逐步跟踪代码调用栈,当SimpleSession过期后,会抛出ExpiredSessionException异常,并被AbstractValidatingSessionManager.validate捕获到,并调用MemorySessionDao中的delete方法将session从map中删除。
MemorySessionDAO代码片段如下:
public void delete(Session session) { if (session == null) { throw new NullPointerException("session argument cannot be null."); } Serializable id = session.getId(); if (id != null) { sessions.remove(id); } }
不知道开发时候脑袋哪根筋掉线,居然初始化SessionManager的配置如下:
@Bean public DefaultWebSessionManager sessionManager(){ DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); sessionManager.setSessionValidationSchedulerEnabled(false); sessionManager.setSessionIdUrlRewritingEnabled(false); return sessionManager; }
把sessionValidationSchedulerEnabled设置成了false,这就意味着清除过期session的scheduler不能生效,也就无法清理map中的session,这个要是不内存溢出,天理难容!!!!!!!
直接贴代码吧
@Bean public DefaultWebSessionManager sessionManager(){ DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); sessionManager.setSessionValidationSchedulerEnabled(true); sessionManager.setSessionIdUrlRewritingEnabled(false); sessionManager.setSessionValidationInterval(DefaultWebSessionManager.DEFAULT_SESSION_VALIDATION_INTERVAL); return sessionManager; }
修改代码10秒钟,问题调研10小时。