堆内存故障诊断需“快速定位+分层验证”:先确认是否真为堆问题(依据oom日志、jstat gc行为、res与-xmx对比),再用jmap三类命令分析结构、对象分布与快照,结合mat/visualvm深入排查泄漏与异常分配,辅以jstat+jstack联动分析线程上下文。

堆内存故障诊断关键在于“快速定位+分层验证”:先确认是否真为堆问题,再区分是配置不足、泄漏还是异常分配。JDK自带工具链足够覆盖绝大多数场景,无需额外安装。
确认堆内存问题是否存在
别一上来就 dump,先排除误判:
- 查日志里是不是明确报 java.lang.OutOfMemoryError: Java heap space —— 这才是堆 OOM 的铁证;Metaspace、Direct buffer、Stack overflow 都不算
- 用 jstat -gc
看 GC 行为:如果老年代(Old)使用率持续 >95% 且 Full GC 后几乎不下降,说明对象在堆积,不是单纯配小了 - 对比 top -p
中的 RES 值和 -Xmx 设置:若 RES 比 -Xmx 高出 1–2GB 以上,要警惕堆外内存干扰,别全往堆上归因
jmap:三类核心用法直击要害
jmap 是堆内存诊断的主力命令,重点掌握这三个组合:
-
jmap -heap
:看堆结构是否合理。重点关注 Eden/Survivor 比例、老年代已用占比、GC 算法是否匹配业务特征(比如高吞吐选 Parallel,低延迟选 G1) -
jmap -histo:live
:找“内存大户”。加 :live 会先触发一次 Full GC,输出的是真正存活的对象。按内存占用排序( sort -k3 -nr),一眼看出哪些类占了 80% 以上空间 -
jmap -dump:format=b,file=heap.hprof
:导出快照。建议在 OOM 前主动执行(或配置 -XX:+HeapDumpOnOutOfMemoryError),避免进程崩溃后丢失现场
可视化分析:MAT 和 VisualVM 不只是看图
导出 .hprof 文件后,不能只靠“饼图”下结论:
- 在 Eclipse MAT 中打开后,先跑 Leak Suspects Report —— 它会自动标记疑似泄漏链,但需人工验证:那个“被 HashMap 引用的 50 万个 User 对象”,是不是本该被清理的缓存?
- 用 dominator tree 查谁真正“掌控”内存:展开 top consumers,看是业务对象本身大,还是它持有的集合(如 List、Map)无节制增长
- VisualVM 适合动态观察:连上运行中的进程,切换到“Monitor”页,勾选“Perform garbage collection”后点“Heap Dump”,能实时比对 GC 前后对象数量变化,验证回收效果
辅助定位:jstat + jstack 联动看上下文
堆问题常和线程行为强相关:
- 当 jstat 显示频繁 Full GC 时,立刻执行 jstack
> thread.log ,搜索 WAITING / BLOCKED 线程 —— 比如大量线程卡在日志打印或数据库连接池获取上,会导致对象长期无法释放 - 结合 jmap -histo:live 找出高频对象后,在 jstack 输出里搜对应类名(如
com.example.CacheManager),看哪些线程正在创建/持有它,定位代码入口 - 如果发现某线程长时间处于 RUNNABLE 状态且 CPU 占用高,再用 jstack 看它在执行什么方法——可能是某个对象构造逻辑极重,或序列化/反序列化吃掉大量内存











