当堆空间仅剩约10%时,我捕获了堆转储。如何检查堆转储以找出导致此问题的内存泄漏或进程、代码。
我尝试理解内存泄漏,运行JMap和Eclipse MAT等工具,但由于缺乏经验,我无法理解这些工具试图展示的内容。或者我应该注意什么?
我有GC前/后的堆转储可以分析。
我已经审核了线程转储,没有“等待锁定”对象线程,线程与下面显示的相似,线程被卡住而没有明显的原因。
在实例视图中显示
显示最近的GC根
,以查看哪个对象仍具有对int数组的硬引用,从而防止其有资格进行GC。假设这是内存泄漏,它可能有助于找到您的内存泄漏。
以下是最近的GC根
示例,可帮助识别我故意添加到程序中的泄漏以展示该想法。如您在屏幕截图中所见,我有一个int数组,无法有资格进行GC,因为它存储在名为leak
的HashMap
中,该HashMap
位于我的Application
类中,因此我知道我的内存问题可能是由于特定的HashMap
引起的,尤其是如果我有许多其他对象导致了这个HashMap
。
NB:当您尝试识别泄漏时,请耐心等待,因为它并不总是显而易见的。理想情况下,您有一个占用整个堆的巨大对象,但显然这不是您的情况,没有什么真正明显的原因,这就是为什么我首先建议调查int数组。不要忘记,它也可能是小的int数组,但有成千上万个具有相同最近的GC根
的数组。
另一个技巧,如果您拥有JProfiler,则可以简单地按照此精彩教程来查找泄漏。
响应更新:
更好地识别内存泄漏根本原因的一种简单方法是至少获取2个堆转储,然后使用类似jhat的工具进行比较,语法如下:
jhat -J-Xmx2G -baseline ${path-to-the-first-heap-dump} ${path-to-the-second-heap-dump}
7000
上启动一个小型HTTP服务器,因此:
Show instance counts for all classes (including platform)
jhat
Exclude weak refs
MAT
的Eclipse Memory Analyzer。
MAT
打开第二个快照histogram
Merge Shortest Paths To GC Roots
/ Exclude All references
仅比较几个转储(例如此处详细说明的Python脚本)似乎是一个过小的样本,因此我编写了一个开源工具(在此处),该工具在后台运行此jmap -histo命令(间隔一定时间)。它有一个实时显示并跟踪每个类的字节计数上升所占的时间百分比。
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=8999
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
-XX:+UnlockCommercialFeatures
-XX:+FlightRecorder
我是Plumbr工具的开发人员之一。除其他功能外,我们还会在内存使用过多时自动分析堆内容。您可能会发现它很有用。
你是否尝试过yourkit profiler?它不是免费的,但你可以评估它30天。在这种情况下,如果你的转储包含所有对象(而不仅仅是活动对象),你将能够检查它们的根。因为可能不是内存泄漏,而是内存占用过大memory footprint。此外,启用GC日志并解析你有多少FullGC暂停也会很好:enable GC logs。
grep "Full GC" jvm_gc.log | wc -l