4、Android内存分析工具概览:选对工具,事半功倍

做内存分析这么多年,我最大的体会就是:没有万能工具,只有合适场景。Android 生态里堆了一堆内存排查工具,每个都有自己的脾气。你如果拿 LeakCanary 去压测大堆,或者用 MAT 去查小泄漏,都会走弯路。

这一节,我把最常用的四款工具——Android Profiler、Heap Dump 导出、LeakCanary、MAT——挨个过一遍。说说它们的强项、短板,以及我实际踩过的坑。

4.1 四款工具一句话定位

先给个总览表格,方便你快速对照。

工具一句话定位我常用的场景
Android Profiler实时监控 + 粗略定位,适合看整体趋势初步排查内存不断上涨的原因
Heap Dump 导出导出 Java 堆快照,供后续离线分析现场拍张堆快照,发给队友或自己回看
LeakCanary自动检测 Activity/Fragment 泄漏,零配置开发期快速发现常见泄漏点
MAT (Memory Analyzer)深度堆分析,找 GC Root 引用链王者定位顽固泄漏,分析 OOM 根因

4.2 Android Profiler:首选「侦察兵」

Android Studio 自带的性能工具,我基本每天都开。但它不是万能的。

  • 优势:实时看内存曲线、线程、CPU,操作简单。能快速判断「内存是不是在持续增长」。
  • 局限:只能看到堆增长趋势,不会告诉你「谁泄漏了」。它的「泄漏检测」功能比较鸡肋,经常误报。

我的习惯:先开 Profiler 跑一遍用例,看曲线是否「锯齿向上」。如果内存只增不减,基本确定有泄漏。这时候我会立刻 dump 堆,而不是盯着 Profiler 猜。

注意:Profiler 本身会占用手机资源。我在项目中遇到过,开启 Profiler 后内存曲线反而变平缓了——因为 GC 被频繁触发,掩盖了问题。所以怀疑泄漏时,建议断开 Profiler 单独测一轮,再 dump 堆。

4.3 Heap Dump 导出:现场的「快照照片」

Heap Dump 就是把 Java 堆全部拍下来,保存成 .hprof 文件。Android Profiler 可以直接导出,也可以用 adb 命令。

# 方式1:通过 Profiler 按钮导出
# 方式2:adb 命令
adb shell am dumpheap <进程ID> /data/local/tmp/dump.hprof
adb pull /data/local/tmp/dump.hprof .

导出后,它只是一个原始数据文件。你还需要用 MAT 或者 hprof-conv 转换才能分析。这里有个坑——Android 的 .hprof 格式和标准 Java 的不完全一样。

# 转换命令(Android SDK 自带的工具)
hprof-conv dump.hprof dump-converted.hprof

高亮笔记:dump 堆的最佳时机是「内存峰值」或「刚刚执行完可疑操作之后」。我曾经在 Activity onDestroy 后马上 dump,才抓到了那个被静态变量持有的泄漏。

4.4 LeakCanary:开发期的「自动报警器」

Square 开源的明星库,Kotlin 项目几乎标配。你只要加一行依赖,它会在 Activity/Fragment 销毁后自动检查弱引用是否被回收——如果没回收,就弹通知、出泄漏路径。

// app/build.gradle.kts
dependencies {
    debugImplementation("com.squareup.leakcanary:leakcanary-android:2.14")
}

我特别喜欢它的泄漏轨迹图,直接告诉你「谁->谁->谁」引用了这个泄漏对象。比如最常见的:

com.example.MyActivity has leaked
  ↓ GC Root
  java.lang.Class (android.app.ActivityThread)
  |
  |持有字段 applicationThread
  |
  |→ android.app.ActivityThread.ApplicationThread
  |  | 持有字段 mActivities
  |  |  → android.app.ActivityThread$ActivityClientRecord
  |     | 持有字段 activity
  |     |→ com.example.MyActivity

曾经踩过的坑:LeakCanary 在 Release 包不会有任何开销,但 Debug 包会多跑一个后台线程。如果你的手机本身就卡顿,建议只在测试机上启用。另外,它无法检测非 Android 组件的泄漏,比如你自己写的单例持有 ViewModel 没释放——这种还得靠 MAT。

4.5 MAT:终极「刑侦利器」

MAT (Memory Analyzer Tool) 是 Eclipse 基金会的神器,我用了快十年。它能把几十 MB 的 .hprof 文件解析成对象引用图,让你找到「泄漏根」。

  • 核心功能:
    • Histogram:列出所有类的实例数、浅堆/深堆大小,一眼看出哪个类「异常多」。
    • Dominator Tree:按「支配树」显示对象占用的「保留堆」,定位大内存黑洞。
    • Path to GC Roots:从泄漏对象反向追溯到 GC Root,这就是泄漏的「铁证」。

我通常的工作流是这样的:

  1. 把 dump 出的 .hprof 用 hprof-conv 转成标准格式。
  2. 在 MAT 里 打开 Leak Suspects Report,它会自动分析出泄漏可能性最大的几个点。
  3. 手动用 Histogram + Path to GC Roots 验证。
  4. 如果有 BLE 相关泄漏,我会直接搜 BluetoothGatt 或 BluetoothDevice 的实例数量。

小诀窍:MAT 的「OQL」(对象查询语言)相当强悍。你可以写 SELECT * FROM com.example.MyClass 来过滤出特定类的所有实例,然后右键「Merge Shortest Paths to GC Roots」——瞬间看到谁引用了它们。

4.6 工具优劣对比总图

下面是我梳理的一张工具选择思维导图,帮你理清不同阶段该用哪个。嗯,注意这张图只画「核心关系」,不覆盖所有细节。

Android 内存分析工具选择逻辑 Android 内存分析工具选择逻辑 待解决:内存泄漏? 开发期快速排查 线上/OOM 深度分析 Android Profiler LeakCanary 实时曲线,定位趋势 自动检测 Activity 泄漏 Heap Dump 导出 MAT 深度分析 保存现场快照 找 GC Root 引用链 箭头起点指向更适合的工具 / 流程

4.7 我的选择建议(避坑指南)

不同阶段用不同工具,别指望一把锤子砸所有钉子。

  • 写代码时:先用 LeakCanary 兜底。它就像家里的烟雾报警器,喷了你就知道。
  • 集成测试/压测:挂上 Android Profiler 看曲线,顺便定时 dump 堆。
  • 查顽固泄漏:dump 堆 -> hprof-conv -> MAT 打开,查 Histogram 搜你怀疑的类名。
  • 线上 OOM:只能用 MAT 或者 Android Studio Memory Analyzer(新版 AS 内置了简易 MAT),没有第二条路。

我最头疼的一次经历:在某款 BLE 手环连接 App 里,LeakCanary 死活检测不到泄漏,但内存每个小时涨 20MB。后来我手动 dump 了三次,用 MAT 对比发现 BluetoothGatt 回调持有了一个 Handler,而这个 Handler 又被匿名内部类引用——典型的「间接泄漏」。

好了,四种工具的核心用法和场景就这些。下一节我们专攻如何使用 MAT 定位 BLE 通信中的 Kotlin 泄漏链——那才是这门课真正硬核的地方。

重要提醒:不同 Android 版本对 .hprof 格式有细微差别。比如 Android 8.0 之后增加了 堆标签,你最好用对应 SDK 的 hprof-conv 工具转换。我在 Ubuntu 上用旧版转换器吃过亏,解析出来的对象数少了一半。