Appearance
JVM 内存问题怎么分析:Heap Dump、MAT、Shallow Heap 和 Dominator Tree 入门
很多人第一次打开 MAT,会被一堆概念直接劝退:
Shallow HeapRetained HeapDominator TreeHistogramLeak Suspects
其实这套工具不需要一上来全学会。先把最核心的几个视角理解清楚,已经足够处理大部分内存排查。
先说结论
分析 JVM 内存问题时,最重要的不是“会不会点 MAT 菜单”,而是搞清楚三件事:
- 哪一类对象数量异常多
- 哪些对象真正占住了大块内存
- 为什么这些对象一直不能被回收
MAT 的价值,就是帮你把这三件事看清楚。
一、什么时候需要 Heap Dump
通常在这些场景下会考虑抓堆快照:
- 内存持续上涨
- Full GC 频繁
- 服务没有明显流量,但堆一直回不去
- 怀疑存在对象泄漏、缓存堆积、集合失控增长
常见命令:
bash
jmap -dump:format=b,file=heap.hprof <pid>如果线上服务负载较高,抓取前要评估影响,不要把排障动作本身变成新的故障来源。
二、先理解四个关键概念
1. Shallow Heap
对象自身占用的内存大小。
比如一个 HashMap 对象本体可能不算大,但它引用的一大串节点、数组、值对象,未必算在它的 Shallow Heap 里。
所以:
Shallow Heap适合看“对象本体大小”- 不适合单独判断“它到底拖住了多少内存”
2. Retained Heap
如果某个对象被回收,连带能一起释放掉的那部分内存大小。
它更接近我们真正关心的问题:
- 这个对象如果没了,系统能腾出多少内存
因此在排查大对象时,Retained Heap 通常比 Shallow Heap 更有参考意义。
3. Dominator Tree
可以把它理解成“内存控制关系树”。
如果对象 B 到 GC Root 的所有路径都必须经过对象 A,那么就可以认为 A 支配 B。
这意味着:
- A 很可能是那一片对象图的关键入口
- 只要 A 还活着,下面那串对象通常就都活着
所以 Dominator Tree 很适合拿来找:
- 谁真正占住了一大片内存
- 哪个集合、缓存、上下文对象在拖着大量数据
4. Histogram
它更像对象分布统计表。
适合先回答这类问题:
- 哪种类的实例最多
- 哪种类总体占用最大
在排查开头,通常先看 Histogram 再深入到 Dominator Tree,效率会更高。
三、MAT 里最常用的几个入口怎么看
Histogram
先看对象数量和总体占用。
适合快速识别:
- 某种 DTO、Map、byte[] 是否异常多
- 是否出现大量重复对象
Dominator Tree
适合定位“谁在真正控制内存”。
如果发现一个大 Map、一个缓存对象、一个会话上下文的 Retained Heap 特别大,通常就值得继续往下钻。
Leak Suspects
它可以作为辅助入口,但不要把它当成最终结论。
因为它更像是:
- 帮你快速列出可疑对象和引用链
真正下判断时,还是要自己回到引用关系和业务代码里验证。
四、一个实用排查流程
可以按这个顺序来:
- 先看
Histogram - 找出数量异常或体积异常的类
- 再到
Dominator Tree看是谁把它们留住 - 顺着
Path to GC Roots看引用链 - 回到代码里判断这是正常缓存、正常上下文,还是确实没有释放
这个流程比“打开 MAT 到处乱点”有效得多。
五、哪些对象最值得优先怀疑
真实项目里,下面几类对象经常是高频嫌疑人:
- 没有限流或淘汰策略的本地缓存
- 过大的
HashMap、ConcurrentHashMap - 大量堆积的
byte[] - 长生命周期集合里挂住的业务对象
- ThreadLocal 未清理导致的线程级残留
尤其当你在 Dominator Tree 里看到:
- 某个集合对象
Retained Heap很大 - 引用链又明显通向缓存、会话、线程上下文
那基本就已经接近答案了。
六、分析 MAT 时容易踩的坑
1. 只看对象个数,不看引用关系
对象多不一定是问题,对象活得不该活才是问题。
2. 只看 Shallow Heap
很多“看起来很小”的对象,背后拖着一大片引用。
3. 把缓存误判成泄漏
不是所有大对象都是内存泄漏。
如果这是一个明确设计过的本地缓存,就要继续确认:
- 是否有上限
- 是否有过期机制
- 当前大小是否符合业务峰值
4. MAT 自己内存配太小
Dump 文件越大,MAT 分析越吃内存。
如果 MAT 自己先 OOM,排查就会变得非常痛苦。
七、和 Java 进程排查怎么配合
如果问题更像:
- CPU 飙高
- 线程阻塞
- 死锁
那就不能只看 Heap Dump,还要结合:
jstacktop -Hp- GC 日志
可以简单记成:
- 堆快照偏向看“对象为什么留住了”
- 线程栈偏向看“线程为什么跑不动了”
一句话总结
MAT 最核心的价值,不是告诉你“哪里可疑”,而是帮你回答:
- 内存都被谁占了
- 谁把这些对象留住了
- 这些引用关系到底合不合理
把 Histogram、Retained Heap 和 Dominator Tree 三件事看明白,JVM 内存分析就已经入门了。