java进程的内存分析 OOM

java项目内存溢出(OOM)的排查方法及原因分析

线上的项目崩了,第一步就是重启服务图片,然后排查下项目日志,没发现异常,继续排查代码,也没找到问题所在,我们真的是太菜了图片,

重启后,几分钟有崩了,瑟瑟发抖图片。

然后开始进程信息排查

先top命令查看了下资源消耗情况,发现异常的java服务cpu使用率达200%,内存占用也极高,所以又通过jstat -gc xx命令查看了下java堆的状况


主要观察的是如下几个指标

YGC:新生代垃圾回收次数

YGCT:新生代垃圾回收消耗时间

FGC:老年代垃圾回收次数

FGCT:老年代垃圾回收消耗时间

GCT:垃圾回收消耗总时间

young gc次数多很正常,gc线程耗时很短,且不会影响其他线程。

而full gc次数也很多且耗时较长就很奇怪了,因为老年代不太可能一下就有这么多内存占用

这才重启多久啊!!!

况且full gc的时候,会发生stop-the-world现象。即除了gc线程外,其他线程都会被暂停,正好和服务进程还在,但info日志不再输出的现象对应!!!

随即通过jmap -dump:format=b,file=dump.txt 进程号 命令,保存了当时的堆快照文件,然后准备进行分析,看看老年代究竟放了些什么对象。

命令:

jmap -dump:format=b,file=dump.hprof 1273315
然后就会保存 进程号 = 1273315 的程序的堆快照文件



然后把这个文件下载下来,用 MemoryAnalyzer 工具进行分析

如何使用MemoryAnalyzer内存分析工具
下载的地址连接在

链接:https://pan.baidu.com/s/1Pi48eZhdEC7rNCKs9QDO_w
提取码:y91z




界面如下














然后内存分析界面如下



详细使用方法完毕

下面是上述提供的生产级别的排除分析图



点击红框处可查看调用方法栈



在当时的分析结果里,图1中堆占用率前三的点击“see stacktrace”都指向同一处业务代码:

调用了mybatis mapper去select,说明有三个线程运行到那处代码时都产生了大对象,直接耗尽了内存,持续引发full gc。

后面分析了那段代码逻辑,是根据用户的某个查询参数去关联表查出商编号,再去商户表里查出对应商户,再遍历组装下数据,再返回给前端。

但是写这段代码的同事,直接把关联表查出的商编号作为参数去商户表查,没有判空,而查商户表时对商编号有如下判断



所以导致如果关联表查出数据为空,这里不会根据customer_no去查,基本等同于全表查询,而商户表有300多万条数据,一次查询会返回一个300多万条数据的list!!!

事故原因复盘
当第一次触发bug时,jvm会把产生的大对象A1分配在新生代(如果此时新生代gc后内存足够的话)。

第二次触发时,新生代gc后内存可能不够放大对象A2了(比如第一次的请求还未结束,A1没有被回收),jvm会直接把A2放在老年代。

第三,第四次触发时,产生的大对象A3,A4也放在了老年代。

可能第五次触发时,老年代内存也不够了,就会触发full gc,一次full gc释放的内存不够,就会继续full gc。

而full gc时,之前还未处理完的请求线程被暂停,大对象还在被引用,无法回收,就会像类似死循环一样,一直full gc了

你学废了。。。。。。









作者:老王

欢迎关注微信公众号 : IT学习道场