生产环境内存泄露(Redirect)的问题排查分析过程

生产环境内存泄露(Redirect)的问题排查分析过程

问题说明

  1. 服务升级框架由原来的Spring-4.0.4.RELEASE+SpringMVC-4.0.4.RELEASE升级为SpringBoot-2.0.2.RELEASE
  2. 服务有原来的外置Tomcat+jsp改成内置Tomcat+jsp

问题分析解决思路

  1. 生产环境过一两天出现内存溢出,观察发现,老年代的内存一直在飙高,触发的Full Gc并不会让老年代的内存减少太多,慢慢的积累,最终导致服务挂掉,内存溢出;
  2. 通过dump生产的堆快照,然后使用MemoryAnalyzer进行堆栈分析,发现问题所在
  3. 找到大量Redirect的内存占用,并且没有的得到释放,出现一定量的ConnectionException(zipkin中对mq的连接验证)并还在不断的增加
  4. 发现大部分堆内存占用都是被Redirect所导致的,还有一部分是被ConnectionException导致的
  5. 修改SpringMvc的Redirect重定向方式,修改zipkin的通信方式(http或者rabbitmq)
  6. 我修改代码放弃了return "redirect:http://www.baidu.com?param=1"的书写方式改成response.sendRedirect(response.encodeRedirectURL(redirectUrl)); return null;这种方式;然后先去掉了zipkin的依赖引用;

分析过程

查看服务运行情况

  1. 查看服务信息(内存占用等情况)

    jps -l[v]

    查看运行的服务的一些基础信息,进程id确定服务(命令的v可选,查看更想起的服务信息)

  2. 使用top 或者top -Hp pid查看内存或者cpu使用情况

    • top:查看多个服务的基础运行情况,内存,cpu的占用情况【可使用z盖面面板颜色,x进行排序操作,shift+<或者>进行不同属性值排序】
    • top -Hp pid:查看某个服务的线程基础运行情况,内存,cpu的占用情况【可使用z盖面面板颜色,x进行排序操作,shift+<或者>进行不同属性值排序】
  3. 使用jmap进行项目内存情况分析(我当时就是根据这个看出老年代内存的不停增加)

    • jmap -heap:查看命令帮助
    • jmap -heap pid:查看进程的内存情况(新生代老年代的占用使用情况)
    • jmap -histo:live pid|sort -k 3 -g -r |less:(查看现在存活的对象内存占用情况,按照占用内存大小进行排序,-g按照数字排序,r反转倒序)【3是内存大小,2是对象数量大小】
    • jmap -dump:live,file=./web_01_202007110930_dump.hprof pid:查看进程为pid存活的堆内存的快照,后续可以用MemoryAnalyzer进行分析或者jprofiler【这两个需要下载下来来分析】进行分析,当然也可以在线上用jhat进行对堆内存分析
  4. 查看服务的gc情况

    -jstat -gcutil 25376 1s [次数]:查看gc的汇总情况,1s为1秒输出一次,次数缺省无限输出,1s如果不写s默认为毫秒可写为1000
    -jstat -gc 25376 1s [次数]:查看gc的详细情况,1s为1秒输出一次,次数缺省无限输出,1s如果不写s默认为毫秒可写为1000

服务分析过程

  1. 首先是通过一段时间( jmap -heap, jmap -histo:live)观察老年代内存的不断增长,查看(jstat )gc的情况
  2. 原本以为是修改之后内存占用多,堆内存由原来的1G修改为现在的2G,后来发现依然出现内存溢出
  3. 进行dump( jmap -dump:live,file=./web_01_202007110930_dump.hprof pid)进行dump线上内存快照,然后使用的是Eclipse的一个插件,这里下载了一个单独版进行分析MemoryAnalyzer,查出问题所在,分析图见下面;
  4. 找到内存泄露点,分别为Redirect的动态参数如return “redirect /index.jsp?userId=111”,后面的参数是动态的,这样Springmvc中间在解析视图的时候会做一个缓存,也会把后面的参数进行缓存,这样每次缓存的路径大部分都是不一样的,时间久了就可能导致内存泄露的问题,晚上也找到的不同的解决方案,最终采用的是该代码的其中一种方案;这里随便从网上搜了一个连接丢到这了
  5. 还有一个点就是观察有一个异常ConnectException异常在不停的增加,通过Dump发现是MQ的连接的异常,刚开始项目引进了Rabiitmq但是没有开启Rabbitmq配置,启动还没有报错,但是在Dump中却发现连接Mq的异常不停的增加,(期间使用SpringBoot的排除RabbitMq的自动配置,删除掉Mq的配置,问题重现),最后通过依赖关系查出猫腻,发现zipkin链路追踪中需要RabbitMq的连接;zipkin可以有选择的http,rabbitnq与zipkin进行通信,这里可以配置通信方式为http,我这边直接把zipkin的依赖暂时去掉了,其他方式自测,这里丢一个链接是解决此方式的方法
  6. 修改Redirect其他跳转方式之后,去掉zipkin依赖之后,问题解决,老年代内存平稳,不在持续增加到很高,并没有在此出现内存溢出;

MemoryAnalyzer内存溢出分析图

  1. 查看内存的基本占用情况生产环境内存泄露(Redirect)的问题排查分析过程
  2. 查看内存占用树情况,观察出问题所在
    生产环境内存泄露(Redirect)的问题排查分析过程