第1章 LeakCanary原理与工作流程:引用链分析、弱引用机制、Android生命周期钩子

1.1 核心原理

LeakCanary 是一款自动检测 Android 应用内存泄漏的工具。其核心原理基于三个关键技术:

  • 弱引用(WeakReference)+ 引用队列(ReferenceQueue):当一个对象只持有弱引用时,GC 可以在下一次回收时将其回收,并将该弱引用放入关联的引用队列。若对象应该被回收但弱引用未进入队列,说明对象仍被强引用持有,即发生泄漏。
  • Android 生命周期钩子:通过注册 ActivityLifecycleCallbacks 和 FragmentLifecycleCallbacks,在 Activity/Fragment 执行 onDestroy() 后立即注入监视对象。
  • 引用链分析:当检测到疑似泄漏时,LeakCanary 触发 Heap Dump,并使用 Shark(自主开发的分析引擎)解析 hprof 文件,从泄漏对象回溯到 GC Root 的最短引用路径,以可视化方式展示给开发者。

1.2 工作流程

一次完整的检测周期包含以下步骤:

  1. 注册监听:在 Application.onCreate() 中初始化 LeakCanary,并注册生命周期回调。
  2. 对象入队:当 Activity/Fragment 销毁时,LeakCanary 创建一个强引用 keyedWeakReference 包裹该对象,并关联一个随机 UUID key,将 key 存入 refWatcher 的待检测集合。
  3. 延时等待 GC:延迟 5 秒(默认)后主动请求一次 GC,然后检查弱引用是否已被回收(即引用队列中是否出现该 weakReference)。
  4. 判定泄漏:若弱引用仍未入队,则认为对象泄漏,触发 Heap Dump。
  5. 分析并展示:解析 Dump 文件,构建引用链,将结果以通知或 Log 形式输出。
关键参数:可通过 LeakCanary.config 修改等待 GC 时长、是否保留 Heap Dump 文件等。

1.3 工作流程内联 SVG

LeakCanary 内存泄漏检测流程 Activity/Fragment 销毁 弱引用 + 引用队列 (keyedWeakReference) 延时 5s → GC 弱引用已入引用队列? 无泄漏,移除监测 Heap Dump Shark 分析引用链 → 通知开发者

1.4 代码接入与配置

在 build.gradle 中添加依赖(以 LeakCanary 2.x 为例):

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

Application 中无需额外初始化,LeakCanary 会自动通过 ContentProvider 注册。如需自定义配置,可在 Application 中调用:

LeakCanary.config = LeakCanary.config.copy(
    // 保留最近 3 次 dump
    retainedVisibleCount = 3,
    // 自定义 GC 等待时间(毫秒)
    watchDurationMillis = 7000L,
    // 是否分析不可达对象
    computeRetainedHeapSize = true
)

1.5 生命周期钩子注册细节

钩子类型 注册方式 监听回调
Activity 生命周期 Application.registerActivityLifecycleCallbacks onActivityDestroyed
Fragment 生命周期 FragmentManager.registerFragmentLifecycleCallbacks onFragmentDestroyed
ViewModel 支持 通过 leakcanary-android-process 自动绑定 onCleared

1.6 常见错误与调试

  • 泄漏误报:如果 Activity 被保留在非静态内部类中,LeakCanary 会报告泄漏。请检查引用链确认是否属于预期保留(如单例持有 Context)。
  • 未初始化:LeakCanary 2.x 自动初始化,若无法检测到泄漏,检查是否正确添加 debugImplementation 而非 implementation。
  • 混淆规则:如果启用了混淆,需添加以下 ProGuard 规则保留引用分析所需类:
-keep class leakcanary.** { *; }
-keep class shark.** { *; }

1.7 性能与稳定性优化

LeakCanary 默认只在 debug 构建中生效,release 构建使用 no-op 空实现。在调试时,Heap Dump 会消耗一定内存和 CPU,建议:

  • 在多进程应用中只保留主进程的 LeakCanary 实例(通过 LeakCanary.config 的 processName 过滤)。
  • 控制保留的 dump 文件数量(retainedVisibleCount)以避免磁盘暴涨。
  • 在低端设备上可增大 watchDurationMillis 以减少 GC 触发频率。
注意:不要在生产环境中开启 LeakCanary 的 Heap Dump 功能,因为 dump 和解析过程可能导致 ANR。始终使用 releaseCompile 的 no-op 版本。