定位内存泄漏需先通过dominator tree按retained heap排序找出支配对象,再结合histogram筛选具体实例,最后用path to gc roots验证其不可回收原因并向上追溯真实泄漏源。

定位支配对象是 MAT 分析内存泄漏的核心环节,关键在于理解“支配”关系——一个对象 A 支配对象 B,意味着从 GC Roots 到 B 的所有路径都必须经过 A;一旦 A 被回收,B 也必然被回收。支配对象往往就是内存无法释放的“卡点”,比如静态容器、线程、线程池或 ClassLoader。
看 Dominator Tree 找出真正“扛大头”的对象
Dominator Tree 按 Retained Heap 降序排列,直接反映每个对象被回收后能释放多少内存。这不是看单个对象大小(Shallow Heap),而是看它“拖着”的整条引用链的总重量。
- 打开 MAT 后点击 Dominator Tree 视图(不是 Histogram)
- 默认已按 Retained Heap 排序,顶部几行就是最可疑的支配者
- 重点关注那些 Retained Heap 占比高(如 >10%)、且不属于业务临时对象的类,例如:
– java.lang.Thread(尤其命名含 “http-nio”、“task-” 的线程)
– java.util.concurrent.ThreadPoolExecutor
– org.springframework.web.context.request.RequestContextHolder
– 自定义的静态 Map 或 CacheHolder - 注意展开节点:有些支配者本身很小(Shallow Heap 几 KB),但它支配着数 MB 的 byte[]、String 或集合,这才是问题所在
结合 Histogram 筛选后,再回 Dominator Tree 定位实例
Histogram 告诉你“哪个类多了”,Dominator Tree 告诉你“哪个具体实例在作祟”。两者要联动使用:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- 在 Histogram 中发现
com.example.OrderService实例数达 5 万、Retained Heap 排前三 → 右键该行 → List objects → with outgoing references - 在新窗口中选中一个典型实例(比如 Retained Heap 最大的那个)→ 右键 → Go to object,自动跳转到 Dominator Tree 中对应位置
- 此时你能看到这个实例是否处于支配链顶端,还是被某个 Thread 或 StaticMap 支配 —— 如果它被 static final Map 引用,那问题根源就在那个 Map 的持有逻辑里
验证支配路径是否合理:Path to GC Roots 是最终判决依据
找到支配对象后,必须确认它为什么不能被回收。右键该对象 → Path to GC Roots → exclude all phantom/weak/soft references:
- 如果路径终点是
java.lang.Thread:检查该线程是否长期存活、是否有未完成的异步任务、ThreadLocal 是否存了大对象 - 如果停在
java.util.concurrent.ThreadPoolExecutor$Worker:说明任务队列堆积,Runnable 持有外部 Service 或上下文未释放 - 若终点是
java.lang.ClassLoader(尤其自定义加载器):大概率存在类卸载失败,关联的 static 字段或监听器仍在引用旧类实例 - 路径中出现
org.springframework.web.context.request.RequestContextHolder:Spring 请求上下文被带入异步线程,导致整个 request scope 对象树无法释放
警惕“伪支配者”:区分真实泄漏点与中间传递者
支配树顶层有时会显示一些系统类(如 java.util.HashMap 或 java.lang.Object[]),它们本身不是源头,只是容器:
- 比如 HashMap Retained Heap 200MB,但它只是被一个 static Map 引用 → 继续向上追,找到持有它的类(如 ConfigManager.INSTANCE)
- 数组对象(byte[]、char[])常排高位,但需看谁在引用它:是日志缓冲区?未关闭的 InputStream?还是 Base64 解码后没清空的临时缓存?
- 用 Merge Shortest Paths to GC Roots 可合并多条路径,快速识别共用的上层持有者,避免逐个排查
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










