定位内存泄漏需先抓堆快照,再用MAT等工具通过Histogram、Dominator Tree和Leak Suspects缩小范围,接着分析强引用链至GC Roots,排除弱/软/虚引用干扰,最后结合代码验证是否遗漏清理逻辑。

分析引用链的核心是定位“谁在拖住不该存活的对象”。不是看对象本身,而是顺着它往上找——直到触达 GC Roots,再判断中间哪一环本该断开却没断。
先抓堆快照,别等 OOM
泄漏发生时,堆内存往往已持续上涨但尚未崩溃。立刻生成堆转储,避免现场丢失:
- 用 jmap -dump:format=b,file=heap.hprof <pid> 手动触发(JDK 自带)
- 或加启动参数 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path,让 JVM 在 OOM 时自动保存
- 生产环境优先用 Arthas:执行 heapdump --live,只导出活跃对象,文件更小、干扰更少
进工具后,聚焦三类视图
打开 MAT 或 VisualVM 后,不急于点“Path to GC Roots”,先用结构化视图缩小范围:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- Histogram(直方图):按类统计实例数和占用内存,快速识别异常增长的类(如某 Listener 实例达上万,或 HashMap 占比突增)
- Dominator Tree(支配树):显示谁真正“占着大块内存”,顶部几个节点往往就是泄漏源头的持有者
- Leak Suspects(泄漏嫌疑人):MAT 自动生成报告,直接标出最可疑的 GC Roots 路径,适合快速切入
查引用链时,关键要排除干扰
右键目标对象选 “Path to GC Roots” 后,务必勾选 exclude all phantom/weak/soft etc. references:
- 弱引用、软引用、虚引用不构成“存活保障”,保留它们会看到大量无效路径,掩盖真正问题
- 重点看强引用链:是否卡在 static 字段(如单例缓存未清理)、ThreadLocalMap.value(线程复用导致 value 滞留)、内部类隐式持外部类(如 AsyncTask 或 Lambda 持有 Activity)
- 若路径终点是 java.lang.Thread,检查该线程是否长期存活且未释放资源;若是 Class 对象,确认静态字段是否误存业务对象
结合代码,验证链路是否合理
工具给出路径只是线索,最终得回归业务逻辑判断:
- 看到 EventBus → subscriber list → MyActivity?检查是否在 Activity onDestroy() 中调用了 unregister
- 看到 ThreadPoolExecutor → Worker → Runnable → OuterClass → large byte[]?确认 Runnable 是否为匿名内部类,是否应改用静态内部类 + 弱引用 Context
- 看到 Thread → threadLocals → ThreadLocalMap → Entry → value?检查是否漏掉 ThreadLocal.remove()
整个过程不是机械点击,而是“假设—验证”:从路径中猜一个嫌疑点,去翻代码确认是否有清理动作缺失。工具负责暴露链路,人负责读懂链路背后的意图与疏漏。

















