导读:笔者最近生产环境出现了 OOM,通过借助 MemoryAnalyzer 对生成的堆转储文件进行分析并找到内存泄露的原因,这里记录做下分享。
关于 MemoryAnalyzer
笔者采用 Eclipse Memory Analyzer Open Source Project | The Eclipse Foundation (简称 “MAT‘)。它是一款强大的 Java 堆转储分析工具,它可以帮助开发人员诊断和解决 Java 应用程序中的内存问题,在定位内存泄漏、优化内存使用等方面提供了很大的帮助(注意 MAT 从 1.9 版本开始 JDK 需 >= 11 才可运行)。
配置
由于 MAT 在分析时需要将堆转储文件加载到内存中进行分析。因此,使用 MAT 分析堆转储文件时需要足够的可用内存。如果可用内存小于堆转储文件的大小,MAT 可能无法完全加载堆转储文件,导致分析结果不准确或者分析过程无法进行。因此为了确保能够正确地加载和分析堆转储文件,建议将可用内存设置为大于堆转储文件的大小。
由于笔者需要分析的堆转储文件大于 20G 左右,本地不足以分析所以将文件放到一台内存足够大的 Linux 服务器上进行分析。
下载并解压 MAT,根据自身场景修改 MemoryAnalyzer.ini 配置文件的 -Xmx 值
-Xmx25600m
分析
执行分析命令如下:
./ParseHeapDump.sh /data/java_pid1.hprof org.eclipse.mat.api:suspects org.eclipse.mat.api:overview org.eclipse.mat.api:top_components
- .hprof:待分析的堆转储文件
- org.eclipse.mat.api:suspects:该插件提供了查找可能存在问题的对象的能力,例如内存泄漏等。这个插件会尝试发现某些对象是否可能导致内存泄漏,并给出相关的建议。
- org.eclipse.mat.api:overview:该插件提供了对堆中所有对象的总体概述,包括对象类型、数量以及占用空间大小等信息。
- org.eclipse.mat.api:top_components:该插件提供了对 Java 堆中最大的对象组件(也称为“瓶颈”)的诊断能力。它会查找并报告占用最多内存的对象组件,帮助开发人员快速找到可能导致性能问题的对象组件。
执行命令时可以根据自身分析需要指定对应插件即可。
解读
经过一段时间的分析后会生成报告(位于被分析的文件同级目录下),如:
- java_pid1_Leak_Suspects.zip
- java_pid1_System_Overview.zip
- java_pid1_Top_Components.zip
报告不大,我们下载到本地后查看分析即可。这里笔者主要关注 suspects,在本地浏览器打开其 index.html 文件。
这份报告给出了前三个导致内存泄露的嫌疑对象,我们可以通过点击 Detauls 进去查看详细情况,这里我们进入查看 Problem Suspect 1。
报告的概述中提到了 ConcurrentHashMap 存在泄漏嫌疑,在 Shortest Paths To the Accumulation Point 中列出导致内存泄漏的最短路径,并指示内存泄漏点(称为“积累点”)。这些积累点是应用程序中未释放的内存对象,并且它们导致了内存泄漏问题。
结合查看 Dominator tree 可以看到内存主要是被 HystrixPropertiesFactory 下的 ConcurrentHashMap 支配(Retained Heap)。
再通过结合代码分析得出泄露原因:在改造连接器时代码设计不当,导致不断往 ConcurrentHashMap 中不断插入元素,导致 ConcurrentHashMap 进行多次扩容操作,从而占用大量内存空间,导致内存泄露。具体为当 ConcurrentHashMap 中元素数量超过某个阈值时,就需要对其进行扩容。默认情况下,ConcurrentHashMap 在每个 Segment 的负载因子达到 0.75 时会自动扩容,将 Segment 中的 Hash 表大小翻倍,以便能够容纳更多的键值对。在扩容过程中,ConcurrentHashMap 会创建新的数组,然后将原来的键值对重新散列到新的数组中。这个过程可能会涉及到大量的内存分配和数据复制操作,很容易导致内存溢出。
标签:分析,文件,ConcurrentHashMap,MAT,OOM,转储,MemoryAnalyzer,内存,Linux From: https://blog.51cto.com/u_16514774/9219034