一, 使用 Spring Boot Actuator 生成 HeapDump 文件
参考我前面的文章 Java | 使用 Spring Boot Actuator 监控应用
访问 http://localhost:1001/monitor/actuator/heapdump 即可生成 heapdump 文件.
二, 安装分析工具 MAT
在 Java 程序运行中发生 OOM 的时候, 我们可以使用强大的内存分析工具 MAT 进行问题跟踪, 但由于习惯了使用 idea 开发, 所以安装 MAT 独立版.
下载地址: https://eclipse.org/mat/downloads.php
MAT Welcome 页面
三, 生成分析报告
首先, 启动前面安装配置好的 Memory Analyzer tool(MAT) , 然后选择菜单项 File- Open Heap Dump 来加载需要分析的堆转储文件.
加载完之后的分析过程如下:
1. 选择 Leak Suspects Report
选择 Leak Suspects Report
2. 点击 Finish, 生成 "内存泄露分析报告"
内存泄露分析报告
3. 从上面的图, 我们查看到内存消耗的整体状况
从上面的 "内存泄露分析报告" 的饼图上, 我们可以清晰地看到一个可疑对象消耗了 2.3G 的内存, 占整个系统的 98% 以上.
4. 继续往下看
内存泄露分析报告下部分
在图的下方还有对这个可疑对象的进一步描述. 我们可以看到内存是由
com.lmax.disruptor.RingBuffer 的实例消耗的, sun.misc.Launcher$AppClassLoader 负责这个对象的加载.
到此, 似乎也没找出根本原因 (对于新手来说), 继续往下看.
5. 点击 "Details »", 如下图所示
进入 Details
我们发现, 产生问题的代码段似乎与 log4j 有一定关系.
6. 进一步定位, 查看 All Accumulated Objects by Class 里面的内容, 如下图
All Accumulated Objects by Class
点击消耗 Heap 最多的 First 10 of 524,168 objects
点击 First 10 of 524,168 objects
7. 查看 First 10 of 524,168 objects 里的内容, 如下图
`First 10 of 524,168 objects
看到这里, 我们发现是有一个大对象 list 存放了大量的 aaaaaaaaaaa_aaaaaa 的值导致的.
我们通过 aaaaaaaaaaa_aaaaaa 在代码上进行搜索, 最后发现, 原来是下面的代码段导致了这个问题:
- String aa = "aaaaaaaaaaaaaaaaaa_aaaaaaaaa_aaaaaaaaa_aaaaaaaaa_aaaaaaaaa_aaaaaaaaa_";
- List<String> list = new ArrayList();
- while (true) {
- list.add(aa);
- log.info("aa:{}", list);
- }
到此, 我们非常顺利地完成了一次 "Java 内存泄漏的排查". 实际上有这个顺利吗? 回答是也大概差不多.
PS: 我在 JVM 第一篇: 一个 Java 内存泄漏的排查案例文章中说过下面一段话:
2.2 找出导致频繁 Full GC 的原因
分析方法通常有两种:
1) 把堆 dump 下来再用 MAT 等工具进行分析, 但 dump 堆要花较长的时间, 并且文件巨大, 再从服务器上拖回本地导入工具, 这个过程有些折腾, 不到万不得已最好别这么干.
2) 更轻量级的在线分析, 使用 "Java 内存影像工具: jmap" 生成堆转储快照 (一般称为 headdump 或 dump 文件).
发现使用 Spring Boot Actuator 可以推翻一下我上面的一点小误解.
感谢你的阅读, 如果对你有帮助就点个赞吧, 这样的鼓励会让我更有兴趣写《JVM 第三篇:******》
来源: http://www.jianshu.com/p/d61e48f5d1c7