泄漏证据采集:使用 Android Studio Profiler 抓取堆转储
3.1 堆转储在泄漏排查中的定位
堆转储(Heap Dump)是内存泄漏排查的核心证据来源。它记录应用进程中所有存活对象的类型、大小、引用链,能直接暴露本应被回收却仍被 GlobalScope 等长生命周期对象持有的实例。本课程第 3 章在“现象识别”之后执行数据采集,目的是将“内存持续增长”这一症状转化为可供分析的快照证据。
采集原则:堆转储应当在泄漏现象最明显的时刻抓取——即“内存上升后GC无法降回基线”的峰值点。过早抓取会漏掉泄漏对象,过晚抓取则因OOM崩溃而无法获取完整现场。
3.2 堆转储采集操作步骤
以下步骤适用于 Android Studio 4.2 及以上版本(基于 Coroutine 场景需注意 Profiler 版本对 Kotlin 协程的支持):
- 选择目标进程:在 Android Studio 底部点击
Profiler窗口,选择“Memory”视图,确认已连接设备/模拟器上目标应用的进程。 - 切换 Memory 面板:点击
Memory时间线选项卡,等待图表开始绘制内存使用量、Java堆、Native堆等曲线。 - 触发可疑操作:在应用内多次执行第 2 章中发现的泄漏路径(如反复打开/关闭某个 Activity 或频繁创建协程任务)。
- 发起 GC:点击 Profiler Memory 面板左侧的
Force GC按钮(垃圾桶图标),触发一次手动垃圾回收,观察内存曲线是否下降。 - 抓取堆转储:在 GC 后内存仍高于基线约 2-3 MB 时,立即点击
Dump Java Heap按钮(照相机图标)。等待 Profiler 完成堆快照生成。 - 保存文件:生成的堆转储以
.hprof格式出现在 Profiler 右侧的Snapshots列表中。右键选择Export to file…保存到本地目录,便于后续离线分析。
// 通过 ADB 命令行也可触发堆转储(适用于自动化采集)
adb shell am dumpheap <process_id> /data/local/tmp/dump.hprof
adb pull /data/local/tmp/dump.hprof ./heap_dumps/
3.3 表格:不同内存采集方式的对比
| 采集方式 | 作用域 | 对性能影响 | 能否发现泄漏 | 推荐场景 |
|---|---|---|---|---|
| Heap Dump(堆转储) | Java/Kotlin 堆中所有对象 | 较大(暂停应用 200-500ms) | 能,直接展示引用链 | 泄漏定位、引用链分析 |
| Allocation Tracking | 记录对象分配栈 | 中等(部分性能开销) | 间接,需配合 Heap Dump | 定位高频分配点 |
| Live Allocation(API 29+) | 无暂停采样跟踪 | 小 | 可观察存活对象变化 | 实时监控内存波动 |
3.4 抓取时刻的判断规则
判断规则表:何时抓取堆转储最有效?
- 规则 A(峰值抓取):手动 GC 后,内存下降幅度 ≤ 总增长量的 30%,且仍高于初始基线 2MB 以上 → 立即抓取。
- 规则 B(重复抓取):至少连续抓取 2 次堆转储,间隔执行 3-4 次泄漏操作,对比两次快照中相同对象的实例数量是否增加。
- 规则 C(避开干扰):抓取前暂停后台定时任务(如网络轮询、动画),避免临时对象污染快照。
- 规则 D(异常捕获):若应用在内存峰值后 OOM 崩溃,使用
android:debuggable=true并在Application.onCreate()中通过android.os.Debug.dumpHprofData()在崩溃前自动 dump。
3.5 内联 SVG:堆转储采集流程
3.6 常见陷阱与规避
避坑清单:
- 单次 dump 误判:泄漏对象可能因 GC 根临时变化而未被识别,务必在不同泄漏阶段连续抓取 2-3 次。
- 忽略 Native 堆:
GlobalScope本身是 Java 对象,但其关联的Continuation可能持有 Native 引用(如 JNI 回调),在Dump Java Heap后额外关注Native Memory面板。 - 快照过大:若堆转储文件超过 200 MB,分析工具(如 MAT)可能卡顿。此时先通过
Force GC清理临时对象,或使用 Android Studio 内置的Heap Viewer快速筛选大对象。 - 遗漏协程上下文:Kotlin 协程的
Job、Continuation对象在堆转储中可能以kotlin.coroutines.jvm.internal.BaseContinuationImpl形式出现,需在分析时将过滤条件设为该基类。
完成堆转储采集后,下一步将进入第 4 章:原因定位——通过 MAT 或 Android Studio 的引用树分析,找出 GlobalScope 具体持有的泄漏对象及其 GC Root 路径。