LeakCanary 原理
利用弱引用(WeakReference)和引用队列(ReferenceQueue)监测对象是否被正常回收,若未被回收则判定为泄漏,然后通过分析堆转储(Heap Dump)文件,找出导致泄漏的引用链。
第一步:检测滞留对象
LeakCanary 会自动“监视”那些生命周期已经结束、本应被垃圾回收(GC)的对象。在 Android 中,主要是已销毁的 Activity、Fragment、Fragment 的 View 以及已清除的 ViewModel 等。
它的检测机制依赖于 Java 的引用类型特性:
- 持有弱引用:当这些对象生命周期结束时,LeakCanary 会将它们传递给一个名为
ObjectWatcher的组件。ObjectWatcher会为每个对象持有一个弱引用。 - 关联引用队列:同时,每个弱引用都会关联一个
ReferenceQueue。根据 Java 的规则,如果一个对象只被弱引用所指向,那么无论内存是否充足,GC 都会回收它,并将这个弱引用本身放入关联的ReferenceQueue中。 - 判断是否滞留:LeakCanary 会等待约 5 秒,然后主动触发一次 GC。之后,它会检查
ReferenceQueue中是否出现了对应的弱引用。- 如果出现了:说明对象已被正常回收,没有泄漏。
- 如果没有出现:说明该对象仍然被其他强引用所持有,无法被 GC 回收,因此被判定为“滞留”(Retained),极有可能存在内存泄漏。
第二步:转储堆内存
当检测到的滞留对象数量达到一定阈值时,LeakCanary 就会采取进一步行动。默认阈值是:应用可见时为 5 个,不可见时为 1 个。
达到阈值后,LeakCanary 会触发一次 Java 堆转储,将当前内存中的所有对象及其引用关系保存为一个 .hprof 文件。这个过程会短暂冻结应用,但这是为了获取完整的内存快照以进行精确分析。
🧠 第三步:分析堆内存
这是 LeakCanary 最核心的分析步骤。它使用自家的 Shark 库(在 LeakCanary 2.0 中引入,替代了旧的 haha 库)来解析 .hprof 文件。
分析的核心目标是找到从 GC Roots 到滞留对象的最短强引用路径,这条路径被称为“泄漏轨迹”(Leak Trace)。它回答了“是谁在一直持有这个本该被销毁的对象?”这个问题。
🏷️ 第四步:分类泄漏
找到泄漏轨迹后,LeakCanary 会利用其内置的 Android 框架知识(以及开发者可自定义的 LeakInspectors),来推断出引用链中哪些对象是真正导致泄漏的“罪魁祸首”。它会对引用链进行“修剪”,将结果聚焦在最可能的泄漏原因上,并将相似的泄漏轨迹归类显示,方便开发者批量修复。
💡 补充:2.0 版本的改进
LeakCanary 2.0 完全使用 Kotlin 重构,核心原理不变,但性能和易用性大幅提升。最大的变化之一是自动安装。开发者只需添加依赖,LeakCanary 就会通过 ContentProvider 自动完成初始化,无需再手动调用 LeakCanary.install()。
总的来说,LeakCanary 的原理可以精炼为:用弱引用“试探”对象是否被回收 → 若未回收则转储内存快照 → 分析快照找出“谁在引用它”的路径。