第 2 章 泄漏数据采集工具全景

当应用出现“内存只增不减”“GC 频繁触发 ANR”或“Flow 订阅者停不掉”等异常时,第一步不是猜原因,而是用正确的工具采集第一手泄漏数据。Android 生态下有四种主力采集手段,本课将按照 现象 → 采集 → 分析 → 复测 → 预防 的排查逻辑,逐一拆解它们的用法和输出含义。

2.1 现象识别:什么时候该采集?

Flow 订阅泄漏往往没有显式异常,但以下几种“红灯”信号必须触发采集动作:

  • 多次返回页面后,空闲内存持续下降(用 dumpsys meminfo 或 adb shell top 观测)
  • GC 日志频繁出现 ConcurrentStarvation 或 Allocation failure
  • LeakCanary 桌面通知弹出(即使没弹出,也应周期性巡检)
  • 使用 Android Studio Profiler 观察到对象计数不减反增

出现上述任意一种,立即进入数据采集流程。

2.2 数据采集:四种工具实战用法

工具 适用场景 采集方式 输出格式 定位精度 上手难度
Android Studio Profiler 开发阶段,实时观察 IDE 内连接设备、点击 Record 火焰图、对象堆、方法调用 中等(显示引用链但需手动过滤) 低
LeakCanary 回归测试、真机日常 依赖注入 + 自动监控 泄漏痕迹(引用链)通知 高(直接给出导致泄漏的引用) 低
dumpsys meminfo 线上/线下快速检查内存水位 adb shell dumpsys meminfo <包名> 纯文本内存统计(PSS/Private/Shared) 低(无引用链,只能判断总量异常) 极低
hprof 文件 深度分析、离线复盘 Profiler 导出 / am dumpheap 二进制 .hprof,需 MAT/Perfetto 打开 最高(可追踪所有 GC Root 引用) 高

2.2.1 Android Studio Profiler 实时采集

  • 操作步骤:在 Android Studio 中连接设备 → 点击 Profiler 面板 → 选择 Memory → 点击 Record。
  • 关键参数:勾选 Record Java/Kotlin allocations;配合 Record native allocations 可排查 JNI 泄漏。
  • 数据输出:录制自动生成对象堆快照,可在 Heap Dump 标签页中按类名搜索,查看实例数量和引用关系。
  • Flow 泄漏线索:搜索 Flow、Continuation、AbstractCoroutine 等类名,看是否有残留实例。

2.2.2 LeakCanary 自动化采集

在 build.gradle 中添加依赖后,LeakCanary 会自动监控 Activity、Fragment、ViewModel 以及自定义 Watcher 注册的对象。当它检测到某个对象在 lifecycle 结束后仍未被 GC,就会生成一个 hprof 快照并解析出引用链。

避坑:LeakCanary 默认只监控 Android 组件生命周期对象。如果需要监控自定义 Flow 订阅者,必须手动调用 LeakCanary.config 注册自定义 ObjectWatcher。
// Kotlin 示例:注册自定义对象监视
LeakCanary.config = LeakCanary.config.copy(
    watcherFactories = listOf(
        SupportFragmentWatcherFactory(application),
        ActivityWatcherFactory(application),
        object : ObjectWatcherFactory {
            override fun create(application: Application): ObjectWatcher {
                return ObjectWatcher(application, name = "FlowSubscriber", 
                    reachabilityInspectors = listOf(FlowSubscriberReachabilityInspector()))
            }
        }
    )
)

当泄漏发生时,Logcat 会打印类似以下信息:

====================================
HEAP ANALYSIS RESULT
====================================
1 RETAINED OBJECTS
* com.example.app.MainViewModel (0x12c)
  - referencing FlowCollector (0x234)
  - referencing ContinuationImpl (0x345)
  - referencing JobSupport (0x456)
  - Root: thread main, system class

2.2.3 dumpsys meminfo 快速水位采集

适合在 CI 或线上容灾场景获取整体内存状态。用法:

adb shell dumpsys meminfo com.example.app -d

关键输出行及其含义:

App Summary
                       Pss(Total)      Private  Dirty     Private  Clean     ...
Native Heap             12345           10000    0                ...
Java Heap               23456           20000    0                ...
... 
Total PSS by OOM adjustment:           ...
  Foreground:       35791 kB
  ...
  • Pss(Total):进程占用的物理内存比例值,越线意味着可疑。
  • Java Heap 区域的 Private Dirty 持续增长通常是 Java 泄漏(包括 Flow 泄漏对象)的征兆。
  • 多次执行并对比 Total PSS,若每次操作后回不到初始值,说明存在泄漏。

2.2.4 hprof 文件深度采集

当 Profiler 或 LeakCanary 给出快照后,你可以导出 .hprof 文件进行离线分析。手动导出命令:

adb shell am dumpheap com.example.app /data/local/tmp/dump.hprof
adb pull /data/local/tmp/dump.hprof ~/analysis/

然后用 MAT(Memory Analyzer Tool)或 Perfetto UI 打开。在 MAT 中:

  1. 选择 Histogram,搜索 Flow、Continuation。
  2. 右键任意实例 → Merge Shortest Paths to GC Roots → 排除软/弱/虚引用。
  3. 查看导致泄漏的引用链,确认是否由 Flow 的 collect 未取消导致。

2.3 原因定位:工具输出配合判断

四种工具的输出各有侧重,但判断泄漏原因时建议遵循以下规则:

  • dumpsys meminfo 只用作“是不是泄漏”的开关,无法定位哪个对象。
  • Profiler / hprof 可找到残留对象和引用链,但需要人工筛选 Flow 相关类。
  • LeakCanary 直接给出 retained object 和最短 GC 路径,几乎不需要二次分析。
常见陷阱:Profiler 堆快照中的 “Shallow Size” 很低不代表没泄漏。Flow 订阅者的 ContinuationImpl 可能只占几十字节,但引用链上的 Job、Scope 可能持有大量外部对象。必须看全部引用路径。

2.4 复测:验证采集是否有效

每次采集后,必须执行一次“复现 → 离开 → 再次采集”的闭环,确认泄漏量是否稳定:

  1. 执行完整泄漏触发场景(如反复进入/退出 Fragment 10 次)。
  2. 使用 dumpsys meminfo 记录 Java Heap PSS 基线。
  3. 等待 5 秒钟(让 GC 运行),再用 Profiler 捕获堆快照。
  4. 对比 10 次前后的对象计数,若 FlowCollector 实例数不等于已销毁实例数,则采集成功,泄漏已捕获。

2.5 预防与自动化策略

  • CI 门禁:在自动化测试后添加 dumpsys meminfo 比较脚本,超标即 fail。
  • LeakCanary 灰度:在内部版本始终开启,重点监控新写的 Flow 页面。
  • hprof 定期导出:通过 am dumpheap 定时采集慢路径(见第 12 章),用于离线回归。
泄漏数据采集决策树 怀疑 Flow 订阅泄漏 dumpsys meminfo LeakCanary Profiler / hprof 导出 MAT / Perfetto 分析 确认泄漏引用链 → 修复 优先使用 LeakCanary
实战建议:在日常开发中,将 LeakCanary 作为第一道防线;遇到漏报时再用 Profiler 手动录制;线上问题必须用 dumpsys 做快速监控,必要时主动 dump hprof 上传进行分析。四种工具组合使用才能不漏掉任意一种 Flow 订阅泄漏。