记Shiro使用过程中的内存不断增加问题修复

1.问题出现

应用部署后,内存会随着使用时长边长,内存逐渐被吃掉,直到最后爬不动。开始研究一下吧......

2.问题分析

首先,dump下来java应用看看到底哪里占用了那么多内存:

记Shiro使用过程中的内存不断增加问题修复

 看到了吧,这个DefaulWebSessionManager居然占用了84%的内存,仔细看一下,主要是MemorySessionDAO中的ConcurrentHashMap中,这个map中为什么这么多的SimpleSession呢:记Shiro使用过程中的内存不断增加问题修复

 相关的继承关系如下:

DefaultWebSessionManager --> DefaultSessionManager --> AbstractValidatingSessionManager

其中sessionDAO在DefaultSessionManager中声明:

记Shiro使用过程中的内存不断增加问题修复

在这个MemorySessionDAO中维护了ConcurrentHashMap。其维护的机制如下:

  • 每当请求通过shiro进行验证时候,均会创建一个SimpleSession,并放到这个ConcurrentHashMap中。
  • AbstractValidatingSessionManager中的两个字段控制了上述map中session的过期并从map中删除
       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);
        }
    }

3.产生原因

不知道开发时候脑袋哪根筋掉线,居然初始化SessionManager的配置如下:

    @Bean
    public DefaultWebSessionManager sessionManager(){
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionValidationSchedulerEnabled(false);
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        return sessionManager;
    }
    

把sessionValidationSchedulerEnabled设置成了false,这就意味着清除过期session的scheduler不能生效,也就无法清理map中的session,这个要是不内存溢出,天理难容!!!!!!!

4.问题修复

直接贴代码吧

    @Bean
    public DefaultWebSessionManager sessionManager(){
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionValidationSchedulerEnabled(true);
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        sessionManager.setSessionValidationInterval(DefaultWebSessionManager.DEFAULT_SESSION_VALIDATION_INTERVAL);
        return sessionManager;
    }

5.总结

修改代码10秒钟,问题调研10小时。

上一篇:spring boot 整合 shiro 权限框架


下一篇:Spring Boot (十四): Spring Boot 整合 Shiro-登录认证和权限管理