第2章 泄漏证据采集:使用 Android Studio Profiler、Memory Analyzer (MAT)、LeakCanary 抓取堆转储
泄漏证据是定位问题的基石。只有获得内存快照(Heap Dump,即堆转储),才能追溯存活对象的引用链。本章聚焦三种主流抓取工具的操作方法、适用场景和产出差异,帮助你快速锁定可疑对象。
2.1 为什么必须采集堆转储
内存泄漏在小型 Demo 中常通过日志或肉眼观察复现,但在复杂应用中需要精确的 对象引用关系图。堆转储就是这一刻进程内所有 Java / Kotlin 对象的快照:它能揭示哪些 Activity、Fragment、Observer 仍被 GC Root 强引用,从而确认泄漏。
LifecycleOwner(如 Activity)在 onDestroy() 后未被回收,且堆转储中该对象实例数 > 0,则说明存在泄漏。实例数 > 1 意味着重复泄漏(每次重建都残留一个)。
2.2 三种工具的工作原理对比
| 工具 | 采集方式 | 产物格式 | 是否自动触发 | 主要用途 |
|---|---|---|---|---|
| Android Studio Profiler | 手动点击「Dump Java heap」 | .hprof(Android 格式) |
否,需要开发者操作 | 实时观察、对比内存趋势 |
| LeakCanary | 自动监测对象回收失败后抓取 | .hprof → 内部分析 → 通知 |
是(自动检测泄漏) | 开发自测、QA 测试、线上自动化 |
| Memory Analyzer (MAT) | 接收 .hprof 文件离线分析 |
转换后 *.hprof(标准格式) | 否,需手动导入 | 深度引用链分析、Dominator Tree 查看 |
2.3 使用 Android Studio Profiler 抓取堆转储
- 运行应用并导航到泄漏场景(例如:打开 Activity → 按返回键)。
- 打开 View → Tool Windows → Profiler,选择目标进程。
- 点击 Memory 图表,稍等数秒使 GC 完成。
- 点击工具栏上的 Dump Java heap 按钮(垃圾桶图标左侧)。
- 等待 Profiler 生成
.hprof并自动显示对象列表。
关键技巧:在 Dump 之前先手动触发一次 System.gc() 或点击 Force garbage collection,排除弱引用、软引用干扰,只保留下真正强引用的对象。
2.4 使用 LeakCanary 自动抓取并通知
LeakCanary 是 Square 开源的自动泄漏检测库。适合集成到 debug 构建变体中,无需手动 Dump。
// app/build.gradle (模块级)
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
// Application.kt
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
// LeakCanary 默认自动初始化;如需自定义,使用 LeakCanary.config
}
}
集成后每次 Activity 或 Fragment 销毁后,LeakCanary 会自动等待 GC 并检查对象是否被回收。若发现泄漏,会在通知栏弹出提示,点击后可直接查看泄漏痕迹(LeakTrace)并导出 .hprof 文件。
若在
release 构建中意外引入 LeakCanary,会导致堆转储频繁触发 → 应用卡顿。
2.5 使用 Memory Analyzer (MAT) 分析标准 HPROF
Android Studio Profiler 导出的 .hprof 文件需经转换才能在 MAT 中使用。
# 使用 Android SDK 提供的工具
$ANDROID_HOME/platform-tools/hprof-conv input.hprof output.hprof
# 或用 Android Studio 自动转换(Profiler 中 Export to standard .hprof)
转换后在 MAT 中打开,使用 Histogram 查看类实例数,使用 Dominator Tree 找到最大的存活对象,右键 Merge Shortest Paths to GC Roots → exclude weak/soft references 查看强引用链。
2.6 故障树:如何选择抓取方式
2.7 堆转储采集排查表
| # | 检查项 | 完成与否 |
|---|---|---|
| 1 | 确保触发泄漏操作前已执行 GC(排除软/弱引用) | □ |
| 2 | Dump 时机:泄漏场景结束后 1-3 秒内(避免二次重建干扰) | □ |
| 3 | 若使用 LeakCanary:确认通知出现且 LeakTrace 包含目标类 | □ |
| 4 | 若使用 MAT:确认已通过 hprof-conv 转换为标准格式 | □ |
| 5 | 检查 HPROF 中目标 Activity 的实例数是否为 0(正常)或 >0(泄漏) | □ |
| 6 | 保存原始 .hprof 文件用于后续对比或分享 | □ |
2.8 常见陷阱与最佳实践
- 陷阱:在低端设备上 Dump HPROF 可能导致 ANR(Application Not Responding)。建议在
debug构建中使用,并关闭所有非必要后台任务。 - 陷阱:AS Profiler 的 Dump 默认不包含 Android 系统对象(如
ViewRootImpl),如需分析系统引用需勾选 Include native objects。 - 最佳实践:每次 Dump 前先记录当前 Fragment / Activity 数量(可通过
adb shell dumpsys activity activities | grep mResumedActivity获取)便于对照。 - 最佳实践:当使用 LeakCanary 发现泄漏但 LeakTrace 不够清晰时,保存其导出的
.hprof并用 MAT 二次分析,可获取更精准的 GC Root 路径。
Lifecycle 过滤条件(在 LeakCanary 2.12 以上版本中会自动标记与 Lifecycle 相关的引用)。若该 Observer 持有 Activity 引用,LeakTrace 将明确显示链路,无需手动翻找。
掌握这三种工具的抓取方式后,下一步将进入 原因定位 环节——从堆转储中提取具体的引用路径,判断究竟是 setValue() 后未释放、还是 observeForever() 未被移除导致。