欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

记录一次Java内存溢出排查过程

程序员文章站 2022-07-15 07:58:50
...

这两天公司的一个程序出现问题,频繁出现内存溢出错误OutOfMemory:GC overhead limit exceeded.

虽然知道这个错误的原因是因为Java虚拟机在频繁进行垃圾回收,使用了98%的时间进行垃圾回收,但是实际回收了不到2%的内存。但结合到代码中,还是无法知道为什么会出现这个问题。

程序的内存设置为3G,6G都不行,快的话10分钟就内存溢出。没有办法,只能给Java程序加上命令行

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\mat.bin

加上之后,如果程序再发生内存溢出,就会在指定位置生成内存映像文件(PS:如果等内存溢出之后,再去手动用jmap生成内存映像文件,可能那时的内存已经释放完了,导出来只有一点点)。

等了几天,程序照常内存溢出,成功生成了内存映像文件。

下载映像文件,使用memory Analyzer软件进行分析。

首先下载memory Analyzer,下载官网是https://www.eclipse.org/downloads/download.php?file=/mat/1.10.0/rcp/MemoryAnalyzer-1.10.0.20200225-win32.win32.x86_64.zip

下载页面可以选服务器,记得选国内的服务器。

下载后解压,记得修改MemoryAnalyzer.ini,把最大内存改大一点,不然都不够mat.bin内存映像文件用。

记录一次Java内存溢出排查过程

记录一次Java内存溢出排查过程

打开MemoryAnalyzer.exe,打开内存映像文件

记录一次Java内存溢出排查过程

记录一次Java内存溢出排查过程

打开后,可以看到,整个程序的大概内存情况,大概用了2.8G,鼠标放上去,显示其中单个类关联的大小就占了2.7个G

记录一次Java内存溢出排查过程

单击饼图,出现菜单,点击List objects -> with outging references

意思是查看这个类里面包含的对象

记录一次Java内存溢出排查过程

 

在这个页面中,点击第三列Retained Heap,让它从大到小排序

可以看到,其中的<Java Local> aaa@qq.com这个变量占用了2.8个G左右

<Java Local>表示是局部变量,也就是线程栈帧中的变量,而不是类的成员变量。

也就是说明,这个ArrayList是在某个方法中生成的,这个方法是关联到AutoReForwardThread线程。

记录一次Java内存溢出排查过程

点开ArrayList,可以看到每个元素的类型是什么,还有总共有4万多个元素,元素的大小有96k

查看这些元素里面的值,大概知道对应的业务模块是什么,然后找到代码,看看是否有一个局部变量List,保存了这些对象

记录一次Java内存溢出排查过程

最后找到这里,这里从数据库中查询出数据,放到list中

记录一次Java内存溢出排查过程

最后验证了数据库,确实查出来几十万条数据,从而导致了Java内存溢出。

总结:List list局部变量在ClassA.method1()中定义,该方法在ThreadB中进行调用,那么list能关联到ThreadB,而关联不到ClassA。所以需要深刻理解Java中的成员变量和局部变量的存放位置,才能明白内存印象文件中类、变量、局部变量、线程中的关联关系。