1.问题现象
tomcat web页面无法访问,日志报出如下错误,
问题出现频率 1晚上1次。
有时候会打印出oom的代码位置,有时候不会打印,不会打印则按照如下流程排查
2.问题排查过程
排查OOM主要是要获取内存的快照文件,但此时jvm已经僵死,无法通过jmap打印内存dump文件。 所以修改tomcat启动脚本增加export JAVA_OPTS 这行增加 如下内容
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/tomcat-heap-dump.hprof
这里表示,当出现oom异常时会自动把当时的内存快照文件打印到/data/tomcat-heap-dump.hprof
2.1修改tomcat启动脚本
// 1.进入docker
docker exec -it tomcat /bin/bash
//2.打开catalina.sh
vi /data/apache-tomcat-9.0.71/bin/catalina.sh
//3.找到export JAVA_OPTS=这行,增加如下内容,保证下次出险oom时,自动打印对快照。
//-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/tomcat-heap-dump.hprof
//4.重启tomcat
docker restart tomcat
2.2分析tomcat-heap-dump.hprof文件
在下次出现oom时,会自动dump出内存快照,使用jprofile打开该快照文件一共35G,打开第一次需要花费的时间比较久。
打开之后如下图所示hashmap的Node有1.98亿
点击 biggest object,看到mybatis返回的list占16G
如果点击biggest object没有大对象占用,那么可以选择group by class,分组可以知道某个对象虽然很小,但是可能某一类却很多。这样也能分析出内存异常占用问题,下图就是这种情况。
右键选择 Use selectd objects
选择如下点击ok
2.3 查看堆栈信息
接2.2最后一步之后,
点击show more,可以看到传入的堆栈信息,发现是我们项目代码造成内存占用这么大
3.最后分析具体代码
经过分析是由于该导出定时任务,分页参数失效做了全表查询,该表在mysql中有5个G。经过导入到内存中可能存储容量就翻倍了,因为堆内存中有其他对象信息加起来可能会比原先的数据量大很多。而且在那部分代码中做了两次全表查询,也就是将这5g数据导入到内存中导入了两次,随后老年代被沾满oom溢出。
该定时一天执行一次,所以一天一次OOM
4.总结
0.看日志,如果日志中打印了代码位置则直接分析相对应的代码,如果分析不出或者没打印则进行如下流程
1.拿到dump文件
2.用jprofile分析
3.找到大对象入口
4.分析具体功能具体代码