GC Root的一些理解
1. 什么是 GC Root
在 JVM/ART 里,垃圾回收的时候会先找一批“根对象”(GC Roots)。
这些根对象是 永远不会被回收的,比如:
静态变量 (static field):因为它们属于类,被类加载器持有,只要类没卸载,就一直活着。
线程栈上的局部变量。
JNI 全局引用。
GC 算法会从 GC Roots 出发,沿着对象引用一路“走下去”,所有能走到的对象都是存活的,不能走到的才会被回收。
2. Kotlin 的 Companion object
本质
在 Kotlin 里:
class ActivityB {companion object {var leaked: ActivityB? = null}
}
编译后其实会生成一个静态内部类:
public final class ActivityB {public static final Companion Companion = new Companion();public static final class Companion {public static ActivityB leaked;}
}
也就是说:
Companion
本身是一个 静态字段,挂在ActivityB
类上。Companion.leaked
又是一个 静态字段,存放了我们赋值的ActivityB
实例。
3. 引用链的含义
当 LeakCanary 分析 hprof 文件时,它会看到:
GC Root → System class
“System class” 就是类加载器持有的类元数据,比如
ActivityB.class
。这属于 GC Root 类型之一。
ActivityB.Companion (static field)
说明
ActivityB
这个类有个静态字段Companion
,一直存活。
Companion.leaked (static field)
Companion
又持有了一个ActivityB
实例。
ActivityB 实例
也就是我们退出的那个页面,但因为被静态引用住了,无法释放。
4. 用人话解释
这条链其实在告诉你:
👉 “因为类 ActivityB
的静态字段 Companion
里有个静态变量 leaked
,它指向了 ActivityB 的实例,所以即使 ActivityB 已经 onDestroy,GC 也没法回收它。”
5. 直观图解
GC Root (ClassLoader 持有 ActivityB.class)↓
ActivityB.Companion (静态字段)↓
leaked (静态字段)↓
ActivityB 实例 ❌ 无法回收,泄漏