jmap 堆 Dump 与对象统计实战
更新时间:2026-09-18。本篇是操作手册;引用链与泄漏类型机制见 jmap 堆转储与对象直方图。
一、先轻量:在线直方图
bash
jps # 找 PID
jmap -histo <pid> | head -30 # 全部对象(含未回收垃圾)
jmap -histo:live <pid> | head -30 # 先 Full GC,只统计存活对象输出列:#instances(实例数)、#bytes(自身字节)、class name。数组元素如 [B 是 byte[]、[Ljava.lang.Object; 是对象数组。先按字节排序看异常多的类,快速初判是不是「某业务对象只增不减」。
二、再精确:导出堆文件
bash
# 传统方式(live 会先 Full GC)
jmap -dump:live,format=b,file=/data/heap-$(date +%s).hprof <pid>
# 推荐方式(JDK8+)
jcmd <pid> GC.heap_dump /data/heap.hprof- 文件大小接近堆大小,先
df -h确认磁盘 ≥-Xmx。 - dump 期间有 STW,堆越大停顿越久;低峰或先摘流量,集群逐台做。
- 不加
live会包含大量垃圾,分析泄漏一般加live(但它会先 Full GC,等于先清一次)。
三、自动留现场(强烈建议)
bash
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/data/dump/
-XX:OnOutOfMemoryError="..." # 可选:OOM 时告警/摘流量脚本四、MAT 分析步骤
- 用 Eclipse MAT 打开 hprof(大堆调大 MAT 自身
-Xmx)。 - 先看 Leak Suspects 自动报告。
- 打开 Dominator Tree,按 Retained Size 排序找「内存霸主」。
- 对可疑对象右键 List Objects → with incoming references 看谁引用,再 Path to GC Roots → exclude weak/soft references 找根。
- 典型根:
static Map/static Cache只进不出、自建缓存无淘汰、ThreadLocal未 remove、监听器/回调未反注册、动态代理导致 ClassLoader 泄漏。
五、常见坑
- 高峰期导堆:STW + 磁盘 IO 拖垮服务,必须低峰/摘流、逐台。
- 看 Shallow 而非 Retained:集合壳很小但持有海量对象,定位要看 Retained Size。
- 磁盘被 hprof 打满:提前预留空间并定期清理 dump 目录。
- 非 live 直方图误判:满屏本该被回收的对象,判断泄漏用
:live。 - dump 和 JDK 大版本不一致:分析工具(MAT/JProfiler)尽量用能识别该 JDK 版本的较新版本。
总结
jmap 定位内存泄漏分两步:先 jmap -histo:live 在线看存活对象实例数和字节 Top 做快速初判,再在低峰/摘流、磁盘预留够的前提下用 jmap -dump:live 或 jcmd GC.heap_dump 导出约堆大小的 hprof,用 MAT 的 Leak Suspects 和按 Retained Size 排序的支配树、配合 Path to GC Roots 追到 static 集合、无淘汰缓存、未 remove 的 ThreadLocal 或泄漏的 ClassLoader;务必配 HeapDumpOnOutOfMemoryError 自动留 OOM 现场,分析时看 Retained 而非 Shallow Size,并注意 live 会先触发一次 Full GC。