1. LeakCanary 内存泄漏检测原理
Android 应用中的内存泄漏是导致 OOM、卡顿和异常退出的主要诱因。LeakCanary 通过自动检测、堆转储(Dump)与引用链分析,将泄漏点精确到类和对象实例,帮助开发者在 CI 流水线中快速捕获回归问题。本章从泄漏根源、工作流程、Dump 与堆分析机制三个层次展开,为后续集成 CI 提供理论基础。
1.1 Android 内存泄漏根源
内存泄漏的本质是短生命周期对象被长生命周期对象持有引用,导致 GC 无法回收。Android 中常见模式:
- Activity 泄漏:静态变量、匿名内部类、Handler、线程或监听器间接持有 Activity 引用。
- Fragment 泄漏:未及时从 FragmentManager 移除的回调或 View 引用。
- 资源未关闭:BroadcastReceiver、ContentObserver、Cursor、Socket 注册后未注销。
- 集合静态化:静态 List、Map 不断添加对象却不清除。
- 单例模式误用:单例传入 Context 作为成员,导致 Context 无法释放。
| 泄漏类型 | 典型场景 | GC 根 Root |
|---|---|---|
| 静态引用 | static 变量持有 Activity | ClassLoader → static field |
| 内部类隐式引用 | 非静态内部类实例持有外部类 this | EnclosingMethod / EnclosingClass |
| 注册未注销 | registerReceiver 未调用 unregister | System Service 回调引用 |
| Thread / AsyncTask | 后台任务持有 Activity 引用 | Thread 引用链 |
提示:LeakCanary 默认只监控 Activity 与 Fragment 的
onDestroy() 后是否被回收。若需监控自定义对象,可通过 RefWatcher.watch() 手动加入观察。
1.2 LeakCanary 工作流程
LeakCanary 的核心工作流分为五个阶段:监控、检测、Dump、分析、报告。整体流程如下图所示:
检测过程中,LeakCanary 内部创建 RefWatcher 并绑定 Android 的 Activity 生命周期。当 onDestroy() 被调用后,对应对象的弱引用进入引用队列。随后 LeakCanary 通过 Runtime.getRuntime().gc() 建议触发 GC,再检查对象是否仍被引用。如果存在强引用路径,则判定为泄漏并启动堆转储。
1.3 Dump 与堆分析机制
LeakCanary 使用 Shark 库(自 v2.0 起内置)代替旧的 hprof 解析引擎,大幅降低内存与分析耗时。Dump 阶段生成 Android heap dump(.hprof)文件,Shark 通过以下步骤定位泄漏:
- 查找泄漏对象:从 Dump 中的
Roots出发遍历对象图。 - 构建最短 GC Root 路径:使用广度优先搜索找到从泄漏对象到 GC Root 的最短强引用链。
- 排除系统泄漏:过滤掉 Android 框架已知的无害泄漏(如 InputMethodManager 等)。
- 生成报告:输出包含类名、字段、引用类型、行号(ProGuard 时可能丢失)的泄漏链文本。
性能优化:CI 环境内存受限时,可配置 LeakCanary 使用
heap-analysis-progressive 模式或设置最大 Dump 大小阈值,避免长耗时分析导致流水线超时。
// build.gradle (app) 添加依赖
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.14'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:2.14'
// Application 初始化
class LeakCanaryConfig : Application() {
override fun onCreate() {
super.onCreate()
if (!LeakCanary.isInAnalyzerProcess(this)) {
LeakCanary.config.apply {
dumpDurationMillis = 5000L // Dump 超时 5 秒
maxDownFileFactor = 0.3f // Dump 文件不超过可用堆的 30%
// 自定义手动观察引用对象
refWatcher?.watch(someLongLivedObject)
}
}
}
}
注意:LeakCanary 的 Dump 操作会暂时冻结应用,CI 环境中建议在测试用例运行完成后再统一分析,避免干扰 UI 自动化测试。可在
LeakCanary.config 中关闭自动 Dump 并调用 RefWatcher.get().triggerManualDump() 按需触发。
在 CI 流水线中,可通过监听 HeapDumpListener 或解析 LeakCanary 保存的 LeakResult 文件,将泄漏信息转成 JUnit 报告,实现“泄漏即失败”的门禁规则。