eclipse mat支配树按retained heap降序排列,顶部对象往往是内存泄漏根因;需结合path to gc roots查静态字段、threadlocal等持有路径,并过滤业务包、对比多dump增长量来精准定位。

在 Java 内存泄漏排查中,Eclipse MAT(Memory Analyzer Tool)的支配树(Dominator Tree)是定位“谁持有了最多对象、谁阻止了对象被回收”的核心视图。它不依赖引用链长度,而是基于对象支配关系——即:若从 GC Roots 到对象 A 的每条路径都必须经过对象 B,则 B 支配 A;B 是 A 的直接支配者(immediate dominator),B 的大小(Shallow Heap)加上其支配的所有对象的 Retained Heap,就构成它的 Retained Heap。支配树按 Retained Heap 降序排列,排在最上面的对象,往往就是内存泄漏的“根因持有者”。
打开支配树视图并理解关键列
MAT 加载 heap dump 后,默认不显示支配树。需手动打开:
- 点击菜单 Navigator → Dominator Tree(或右键 heap dump → Open Reports → Dominator Tree)
- 视图默认按 Retained Heap 降序排列,重点关注顶部几行
- 关键列说明:
• Class Name:类名,帮助识别对象类型(如 HashMap、ArrayList、自定义 Service)
• Objects:该类实例总数(注意不是单个对象,而是所有同类实例的 Retained Heap 总和)
• Retained Heap:该对象及其支配子树所占总内存(即:若它被回收,能释放多少内存)
• Shallow Heap:对象自身占用内存(不含引用对象),通常远小于 Retained Heap
结合支配路径快速定位泄漏源头
仅看支配树顶部可能不够——比如一个 HashMap 占用高,但它本身未必是泄漏点,真正的问题可能是它被某个静态字段长期持有。这时要查支配路径:
- 在支配树中右键目标对象 → Path to GC Roots(推荐选 with all references)
- 重点观察路径中是否出现:
• static 字段(如MyCache.INSTANCE、LogFactory.loggerMap)
• ThreadLocal 变量(尤其在线程池中未清理)
• 未注销的监听器/回调(如 Swing Listener、Spring ApplicationListener)
• ClassLoader 持有大量类实例(常见于热部署失败后) - 若路径终点是
java.lang.Thread @0x…且线程仍存活,检查该线程是否持有业务对象(如ThreadLocal<map></map>)
过滤与对比:排除干扰、聚焦真实泄漏
生产 heap dump 常含大量系统类或临时对象,需主动过滤:
- 在支配树顶部输入框输入过滤条件,例如:
•com.yourcompany.*(只看业务包)
•-java.* -javax.* -sun.*(排除 JDK 类) - 使用 Compare Basket 功能:加载多个 dump(如 t1 和 t2),对比相同类的 Retained Heap 增长量,增长显著的类大概率是泄漏主体
- 对可疑对象(如某 ArrayList),右键 → List objects → with incoming references,查看谁在引用它,验证是否被意外长期持有
注意支配树的局限性与常见误判
支配树强大但非万能,需避免典型误区:
- Retained Heap 高 ≠ 一定泄漏:比如缓存类设计本就应长期持有数据,需结合业务逻辑判断是否“合理”
- 支配关系是瞬时快照:dump 时刻的支配结构不代表运行时持续存在,需配合代码逻辑与日志验证
-
数组/集合类容易误读:一个
Object[]Retained Heap 很大,实际可能是它持有的元素对象多——此时应点开该数组,再看其元素的支配关系 - 若支配树几乎为空或报错“Cannot compute dominator tree”,说明 dump 文件损坏或 MAT 版本兼容问题,尝试用最新版 MAT 重解析
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











