LeakCanery 浅析理解

12 阅读1分钟

leakCanery 是检测Android内存泄漏的工具

Android中内存泄漏的本质:Android (或 JVM)的内存泄露:短⽣命周期的对象被⻓⽣命周期的对象持有, 从⽽导致短⽣命周 期的对象不能被释放

1. 第一步:标记 “待验证对象”(Activity)

当 Activity 执行 onDestroy() 时,LeakCanary 认为它 “应该进入可回收状态”,

为这个 Activity 创建 KeyedWeakReference(绑定 ReferenceQueue)

第二步:触发 GC,让虚拟机做 “可达性分析”

LeakCanary 主动调用 Runtime.getRuntime().gc(),触发虚拟机执行可达性分析(GC 的核心工作):

  • 虚拟机从 GC Roots(静态变量、主线程栈帧、JNI 引用等)出发,遍历所有对象的引用链;
  • 如果遍历后发现:没有任何强引用指向这个 Activity → 判定 Activity 为 “不可达”,标记为 “可回收”;
  • 如果有强引用(比如单例持有 Activity)→ 判定 Activity 为 “可达”,标记为 “不可回收”。

第三步:通过 “弱引用是否入队” 验证结果

虚拟机完成可达性分析后,会按规则处理弱引用:

  • 如果 Activity 不可达(可回收)

    虚拟机先销毁 Activity,释放内存,同时把绑定的 KeyedWeakReference 加入 ReferenceQueue(这是虚拟机的 “通知行为”);

    LeakCanary 检查队列,发现这个弱引用 → 判定 “Activity 已被回收,其内部成员变量也跟着被回收了”。

  • 如果 Activity 可达(不可回收)

    虚拟机不会销毁 Activity,也不会把弱引用加入队列;

    LeakCanary 检查队列,没找到这个弱引用 → 判定 “Activity 还被强引用持有,无法回收,其内部成员变量也跟着泄漏”。

三、关键补充:LeakCanary 如何定位成员变量的泄漏?

当判定 Activity 泄漏后,LeakCanary 会通过堆快照(.hprof)做两件事:

  1. 从 GC Roots 出发,找到指向 Activity 的强引用链(比如 “静态单例 → Activity.mPresenter → Activity”);
  2. 解析堆快照中的对象结构,展示 Activity 内部哪些成员变量被连带引用(比如 mPresenter 被单例持有,导致整个 Activity 无法回收)。

简单说:LeakCanary 先通过弱引用验证 Activity 是否泄漏,再通过堆分析找到泄漏的 “罪魁祸首”(可能是某个成员变量被外部强引用)

垃圾回收机制

垃圾回收机制分为「引⽤计数法」和「可达性分析法」

引⽤计数法:⽤⼀个计数器记录⼀个对象被引⽤的次数,如果引⽤的次数被减少到 0 那么说 明这个对象是垃圾 对象。 都是引⽤计数(引⽤计数有循环引⽤的问题)

**可达性分析法:Jvm 通过⼀些 GC Roots 向下搜索,如果可以被 Gc Roots 引⽤到的对象,说明 这个对象不是垃圾 对象,反之这个对象就算互相引⽤了也是垃圾对象 那些对象 会被作为 GC Roots 呢
**

在线程栈中的局部变量,也就是正在被调⽤的⽅法,它⾥⾯的参数和局部变 量

存活的线程对象

JNI 的引⽤

Class 对象,因为 Android 加载 Class 后不会卸载 Class

引⽤类型的静态变量

四⼤引⽤:

强⼀点的引⽤: 强引⽤——不会被垃圾回收

弱⼀点的引⽤:

                   弱引⽤——可以通过 get() 获得引⽤对象,会被垃圾回收 

                   软引⽤——可以通过 get() 获得引⽤对象,内存不⾜会被垃圾回收 

                   虚引⽤——不能通过 get() 获得引⽤对象,会被垃圾回收