MAT分析Java内存泄漏核心是两步:先通过Histogram和Dominator Tree定位大对象(关注Retained Heap),再用Path to GC Roots追踪引用链确认泄漏原因;Leak Suspects报告可快速聚焦高风险点。

用 MAT 分析 Java 内存泄漏,核心是两步:先定位“谁占得多”,再搞清“为什么活得好”。大对象本身不等于泄漏,但它是泄漏的常见载体;引用链则直接回答“它为什么没被回收”。下面分三块说清楚怎么实操。
快速定位大对象:从 Histogram 和 Dominator Tree 入手
打开 .hprof 文件后,优先看两个视图:
- Histogram(直方图):按类统计实例数和内存占用。点击“Retained Heap”列降序,重点关注排在前面、实例数异常多或单个 Retained Heap 超大的类(比如某个 DTO 实例占了几百 MB)。
- Dominator Tree(支配树):它按“谁保留了最多内存”排序,顶层节点就是内存压力的主要来源。展开后能看到某 Map、某缓存容器、甚至某个线程实例长期持有了大量子对象——这往往是泄漏起点。
注意:不要只盯着“Shallow Heap”,那是对象自身大小;真正关键的是“Retained Heap”,即该对象被回收后能释放的总内存。
追踪无法回收的原因:Path to GC Roots 是关键动作
选中可疑对象(比如一个涨到上万实例的 UserContext),右键 → “Path to GC Roots” → 选 “exclude weak/soft references”。MAT 会生成一条从该对象回溯到 GC Roots 的完整引用链。
立即学习“Java免费学习笔记(深入)”;
这条链里藏着泄漏真相:
- 如果路径里出现 static final HashMap 或 ConcurrentHashMap,大概率是缓存没清理;
- 如果经过 ThreadLocalMap → 某个线程 → 线程池里的 Worker,说明 ThreadLocal 没 remove;
- 如果路径终点是 java.lang.ref.Finalizer,可能是资源未关闭(如未 close 的 InputStream);
- 如果看到匿名内部类或 Handler 持有 Activity/Fragment,在 Android 场景中很典型,Java 后端则可能是非静态内部类意外延长了外部类生命周期。
借助 Leak Suspects 报告缩小排查范围
启动分析时,MAT 默认会运行自动检测。点开 “Leak Suspects” 报告,它会用启发式算法识别出几个高风险区域,并附带简要说明和引用链快照。
这个报告不是结论,而是高效入口:
- 它常能指出“某 static Map 持有 2.4GB 对象”,并直接跳转到该 Map 实例;
- 报告里标红的“Accumulated Objects”数字,代表该嫌疑点下所有不可达但被持有的对象总量;
- 别全信报告结论,但一定要点进去看它给出的 Path to GC Roots,再结合业务代码确认是否合理。
不复杂但容易忽略


















