4. 内存分析工具(二):MAT(安装、使用与泄漏分析
聊完抓取堆转储文件,接下来就是真正的重头戏——怎么分析这些成百上千兆的 dump 文件。我个人习惯用 MAT,这东西虽然叫 Memory Analyzer Tool,但你把它当“内存侦探事务所”来用就行。它能帮你从一堆对象里揪出那个“该回收却死活不回收”的嫌疑人。
核心逻辑:MAT 把堆快照解析成对象图,然后通过支配树、GC 根路径、OQL 查询等机制,替你找到泄漏点。说白了就是“谁占着茅坑不拉屎”一目了然。
4.1 安装与配置
MAT 是 Eclipse 基金会的开源项目,最新版可以从官网下载。嗯,这里要注意:MAT 本身需要 JDK 11 及以上才能跑,而且你最好给它分配至少 4GB 的堆内存——不然你分析一个 2GB 的 hprof 文件,它自己就先 OOM 了。
修改启动配:打开 MemoryAnalyzer.ini,找到 -Xmx 参数,改成 -Xmx4096m。我在项目中遇到过因为没改这个参数,导致 MAT 直接闪退,白白浪费半天时间。
4.2 导入堆转储文件
把从设备 dump 出来的 .hprof 文件拖进 MAT 窗口就行。如果你用 Android Studio 的 Profile → Dump Java Heap 得到的文件,可以直接用;如果是用 adb shell am dumpheap 抓的,注意要先转换成标准格式:
# 使用 hprof-conv 转换
hprof-conv raw_dump.hprof standard_dump.hprof
platform-tools/ 目录下。我第一次转换时忘了指定输出文件名,差点以为工具坏了。4.3 基础分析操作
导入成功后,MAT 会弹出“泄漏嫌疑报告”(Leak Suspects Report)。别急着点“Finish”,先看看基本信息:
- Total size:堆总大小,跟你预期是不是一致。
- Class loader / unreachable objects:通常不重要,可以关掉。
然后我建议你点一下 Keep Unreachable Objects,虽然会增大分析量,但有时候泄漏的对象恰好是从不可达状态变成可达的,排查更彻底。
核心操作就两个视图:
- Histogram:按类的实例数排序,快速找出哪个类创建了海量对象。
- Dominator Tree:显示对象间的支配关系,最大的“支配者”往往是泄漏的根。
你想想看,如果某个 Activity 实例数远远超出你的预期(比如超过预设的 1 个),那大概率就是泄漏了。为什么?因为正常情况下 Activity 销毁后会被 GC 回收掉。
4.4 泄漏定位实战
我拿一个真实的案例说明。当时公司的新闻列表页,用户反复进入退出,内存暴涨。我用 Histogram 一看,NewsListActivity 的实例数居然有 17 个。
| 步骤 | 操作 | 结果 |
|---|---|---|
| 1 | 右键 NewsListActivity → Merge Shortest Paths to GC Roots → exclude all phantom/weak/soft references | 只显示强引用路径 |
| 2 | 观察路径中的对象 | 发现被一个静态的 Handler 持有 |
| 3 | 检查该 Handler 的 target | 是匿名内部类,隐式持有外部 Activity 的引用 |
| 4 | 添加 Handler.removeCallbacks() 即可修复 |
还有一种常见场景:Bitmap 泄漏。用 Histogram 看 Bitmap 对象的总 bytes 占用,如果反复操作后 bytes 没降,多半是某个 Context 或 View 持有了 Bitmap 没释放。
4.5 OQL 查询
MAT 自带的 OQL(对象查询语言)很像 SQL,适合按特定条件过滤。比如我想找所有大于 1MB 的字节数组:
SELECT * FROM byte[] b WHERE b.length > 1024*1024
SELECT toString(s) FROM java.lang.String s WHERE toString(s) LIKE "%leak%" 来快速找到可疑字符串。⚠️ 注意:OQL 查询耗内存,不要一次返回几十万行,尽量用 LIMIT 100 截断。
4.6 避坑指南
- 我曾经 因为直接打开生产环境 3GB 的 hprof,MAT 卡死半小时。之后我学乖了:先减小 dump 范围,或者只分析特定类。
- 不要忽略 WeakReference:有些泄漏是 WeakReference 被意外清除后又重新被强引用持有,需要结合代码逻辑判断。
- 对比两次 dump:如果你有操作前后的堆快照,用 MAT 的 Compare Tables 功能,能非常直观地看到哪些对象增加了。
- 别只信 Leak Suspects Report:那个报告是启发式的,有时会误报。我建议把它当线索,最终还是要靠 GC Root 路径确认。
MAT 的学习曲线其实不陡,关键是亲自动手走一遍流程。下次你再遇到内存抖动或 OOM,不用慌,打开 MAT,按上面五步走,90% 的泄漏都能揪出来。