解决由于jvm内存泄漏导致的频繁fullgc带来的tomcat假死问题分析

问题现状:系统运行期间突然出现tomcat假死,因为系统很久没改代码,以为是访问量增加带来的内存导致,改大内存后观察,用jstat观察系统平稳运行,old区稳定增长。

问题分析:
利用jvisualvm查找jvm垃圾不回收内存泄漏fullgc导致的tomcat假死问题分析

于是写个脚本监控,内存是否还会暴涨,顺便重启下系统预防tomcat假死
#!/bin/bash
export JAVA_HOME=/u01/install/java
export CLASS_PATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
export PATH=$JAVA_HOME/bin:$PATH

pid=ps -aux| grep "java" | grep "tomcat-stweb-web" | awk '{print $2}' ;
old=/u01/install/java/bin/jstat -gcutil $pid | awk 'NR==2{print $4}' ;
old=${old//./ };
arr=($old); echo ${arr[0]} ;
if [ ${arr[0]} -gt 60 ] ;
then
/usr/bin/kill -9 $pid ;
/u01/install/tomcats/tomcat-stweb-web/bin/startup.sh ;
fi

一段时间之后,还是发现tomcat重启了,也就是说不是内存不够的问题,于是只能dump出内存暴涨时的内存快照,在上面代码中加入
/u01/install/java/bin/jmap -dump:live,format=b,file=/u01/install/${pid}.hprof $pid ;在tomcat重启之前dump出当时的内存快照文件到本地,使用jvisualvm分析,注意分析内存快找电脑必须够强悍,至少16G内存要不很难转储内存快照文件。

启动jvisualvm 导入快照文件

利用jvisualvm查找jvm垃圾不回收内存泄漏fullgc导致的tomcat假死问题分析

查看内存中但是哪些类的对象数最多,此时对象数一般就是引起内存暴涨的根本原因,要么是程序bug导致的内存泄漏,要么就是对象来不及回收

利用jvisualvm查找jvm垃圾不回收内存泄漏fullgc导致的tomcat假死问题分析

上图中SimpleProdStat 和 Image 对象是自定义对象里面最多的,应该是异常(如果分析这个到底是不是bug,可以在程序内存没有暴涨正常情况下dump出快照分析看看有无此对象,如果有此对象但是个数保持稳定个数也正常,要是异常暴增应该考虑是是内存没回收导致)

继续查看该对象内存回收的根如下图:

利用jvisualvm查找jvm垃圾不回收内存泄漏fullgc导致的tomcat假死问题分析

利用jvisualvm查找jvm垃圾不回收内存泄漏fullgc导致的tomcat假死问题分析

如上图该个数最多的对象,垃圾回收的根节点如下图:

利用jvisualvm查找jvm垃圾不回收内存泄漏fullgc导致的tomcat假死问题分析

利用jvisualvm查找jvm垃圾不回收内存泄漏fullgc导致的tomcat假死问题分析

回收的根结点为ArrayList,下面看看这个ArrayList在在哪里被调用可以查看该对象的线程调用栈

利用jvisualvm查找jvm垃圾不回收内存泄漏fullgc导致的tomcat假死问题分析

利用jvisualvm查找jvm垃圾不回收内存泄漏fullgc导致的tomcat假死问题分析

在上图标红的框就是刚才ArrayList内存在线程中调用,继续往下看,找到最终调用的业务方法

利用jvisualvm查找jvm垃圾不回收内存泄漏fullgc导致的tomcat假死问题分析

问题解决:

上图标红的地方为业务调用处,打开eclipse,发现这里一次从数据库查询一个用户的simpleprodstat对象,很多都为几万几千个对象一次查询到账jvm内存瞬间爆满而带来的fullgc导致的tomcat假死,修改业务改成分页查询就可以解决问题。