第3章:泄漏检测工具:Android Studio Profiler使用、Memory Analyzer (MAT) 基础、LeakCanary集成

内存泄漏这东西,光靠看代码很难揪出来。你想想看,代码写得再漂亮,跑起来该泄漏还是泄漏。所以,工具就是我们最好的眼睛。这一章,我带你过一遍三个最常用的检测工具:Android Studio Profiler、MAT 和 LeakCanary。每个工具都有自己的脾气,用对了地方,事半功倍。

3.1 Android Studio Profiler:实时监控的瑞士军刀

我个人习惯,开发阶段一直开着 Profiler。它就像汽车的仪表盘,随时告诉你内存、CPU、网络的状态。尤其是内存那一栏,能直观看到堆内存的起伏。

3.1.1 怎么用?

  1. 打开方式:点击底部「Profiler」标签,或者用快捷键 View → Tool Windows → Profiler
  2. 选择进程:连上设备或模拟器,选中你的应用进程。
  3. 看内存曲线:点击「Memory」那一行,就能看到实时堆内存占用。

这里有个小技巧:手动触发 GC。你点一下 Profiler 面板上的「垃圾桶」图标,强制垃圾回收。如果回收后内存还是居高不下,那八成有泄漏了。

我的经验:我在项目中遇到过,某个页面退出后,内存曲线死活降不下来。点了几次 GC,发现有个 Bitmap 对象一直活着。后来一查,是静态变量持有了 Activity 的引用。嗯,这种问题用 Profiler 一眼就能看出来。

3.1.2 抓取 Heap Dump

光看曲线不够,得抓堆转储文件(Heap Dump)。操作很简单:

  1. 在 Profiler 面板上,点击「Dump Java Heap」按钮。
  2. 等几秒钟,会生成一个 .hprof 文件。
  3. 你可以直接在 Android Studio 里分析,也可以导出给 MAT 用。

为什么要抓 Dump?说白了,就是给内存拍一张「X光片」。你能看到每个对象占了多少内存,谁引用了谁。泄漏的根源,往往就藏在这些引用链里。

注意:抓 Dump 的时候,应用会卡顿一下。别在生产环境频繁抓,影响用户体验。我一般只在 Debug 版本里做。

3.2 Memory Analyzer (MAT):深度分析的利器

Android Studio 自带的分析器够用,但遇到复杂泄漏,还是得请出 MAT。这工具是 Eclipse 基金会出的,专门分析 Java 堆转储文件。它最牛的地方是:自动帮你找泄漏嫌疑对象

3.2.1 安装与导入

  1. 去 Eclipse 官网下载 MAT(Memory Analyzer Tool)。
  2. 把 Android Studio 导出的 .hprof 文件,用 MAT 打开。
  3. 注意:Android 的 hprof 格式和标准 Java 不一样。你需要用 hprof-conv 工具转换一下。这个工具在 Android SDK 的 platform-tools 目录下。

转换命令很简单:

hprof-conv input.hprof output.hprof

转换完,再用 MAT 打开 output.hprof

3.2.2 核心功能:Leak Suspects

打开文件后,MAT 会自动分析,弹出一个「Leak Suspects」报告。它会列出最可能泄漏的对象,以及它们的引用链。我一般先看这个,能省不少时间。

举个例子:

  • 报告里说「一个 Activity 实例被一个静态 Handler 持有」。
  • 点进去,能看到完整的引用路径:Handler → Message → Activity
  • 这时候你就知道,Handler 内部类持有了外部 Activity 的引用。
避坑指南:我曾经被 MAT 的「Shallow Heap」和「Retained Heap」搞晕过。简单说:
- Shallow Heap:对象本身占的内存(不含引用对象)。
- Retained Heap:对象被回收后,能释放的总内存(包含它引用的所有对象)。
查泄漏,主要看 Retained Heap。值越大,泄漏越严重。

3.2.3 常用操作

操作 说明
Histogram 列出所有类的实例数量和占用内存。按 Retained Heap 排序,找大块头。
Dominator Tree 显示对象的支配关系。谁占内存最多,一目了然。
Path to GC Roots 从 GC Roots 到目标对象的引用链。泄漏的「罪魁祸首」就在这条链上。

我个人习惯,先用 Histogram 找到可疑类,再用 Path to GC Roots 追根溯源。这两个组合,基本能搞定 90% 的泄漏问题。

3.3 LeakCanary:自动化泄漏检测的守护神

前面两个工具都是手动分析,适合排查疑难杂症。但日常开发中,我们更需要一个「自动报警器」。LeakCanary 就是干这个的。它集成到应用里,一旦检测到泄漏,直接弹通知告诉你。

3.3.1 集成步骤

build.gradle 里加依赖:

dependencies {
    debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
    releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:2.12'
}

注意:debugImplementation 只在 Debug 版本生效。Release 版本用 no-op,不会影响性能。

集成完,不需要写任何初始化代码。LeakCanary 会自动注册一个 ContentProvider,在应用启动时初始化。你只管跑应用,它会在后台默默监控。

3.3.2 怎么用?

  1. 正常操作你的应用,比如打开一个 Activity,再返回。
  2. 如果 LeakCanary 检测到泄漏,会弹出一个系统通知。
  3. 点开通知,能看到泄漏对象的引用链,和 MAT 的 Path to GC Roots 类似。

举个例子:你打开了一个详情页,然后按返回键。LeakCanary 发现这个 Activity 没有被回收,就会通知你。引用链可能显示:Fragment → View → Activity。这时候你就知道,Fragment 里某个 View 持有了 Activity 的引用。

我的建议:LeakCanary 适合集成到所有项目的 Debug 版本里。它不费什么资源,但能帮你提前发现很多问题。我在团队里推行过,效果很好。以前要花半天排查的泄漏,现在几分钟就能定位。

3.3.3 注意事项

  • 不要在生产环境用:LeakCanary 会占用一些内存和 CPU,影响性能。Release 版本一定要用 no-op
  • 理解误报:有时候 LeakCanary 会报告「假阳性」。比如系统缓存的对象,或者第三方库的持有。这时候需要你手动判断。
  • 配合 MAT 使用:LeakCanary 只能告诉你「有泄漏」,但深层次原因还得靠 MAT 分析。我一般先用 LeakCanary 发现泄漏,再用 MAT 抓 Dump 深入分析。
避坑指南:我曾经遇到一个情况,LeakCanary 一直报某个 Fragment 泄漏,但引用链指向了系统 Service。后来发现是 Android 版本 bug,不是我们代码的问题。所以,别盲目相信工具,要结合代码逻辑判断。

3.4 三个工具怎么选?

说白了,各有各的用处:

  • Android Studio Profiler:适合日常开发,实时监控内存变化。快速定位「哪个页面泄漏了」。
  • MAT:适合深度分析,查复杂引用链。尤其适合大堆转储文件的分析。
  • LeakCanary:适合自动化检测,集成到项目里,让泄漏无处遁形。

我个人习惯是:LeakCanary 常驻 Debug 版本,Profiler 随时开着,MAT 作为终极武器。三个工具配合使用,基本能覆盖所有泄漏场景。

嗯,这一章就到这里。工具只是手段,关键还是理解内存泄漏的原理。下一章,我们聊聊常见的泄漏场景,比如静态变量、Handler、匿名内部类这些「老熟人」。到时候,你会发现自己排查泄漏的能力又上了一个台阶。