GC Roots是一组必须活跃的引用而非对象,作为可达性分析起点:从它们出发可达的对象存活,不可达者被回收;包括虚拟机栈局部变量、方法区静态/常量引用、JNI引用及运行中线程等。

JVM 垃圾回收中,根集合(GC Roots)不是具体的一组对象,而是一组必须活跃的引用,作为可达性分析的起点。只要一个对象能通过引用链从这些根引用到达,它就被视为“存活”;反之,不可达的对象即被判定为垃圾,可被回收。
GC Roots 的本质是活跃引用,不是对象本身
这点容易误解。例如,栈帧里的局部变量 a 是一个引用,它可能指向堆中某个 Object 实例;这个引用 a 才是 GC Root,而不是它所指向的那个对象。JVM 关注的是“谁还在用它”,而不是“它长什么样”。因此:
- GC Roots 是引用,不是对象实体
- 它们必须始终处于活跃状态(如正在执行的方法中未出作用域的变量)
- 一旦引用失效(如方法返回、变量越界),它就不再属于 GC Roots
常见的 GC Roots 类型
这些引用来源覆盖了 JVM 运行时最核心的活跃区域:
- 虚拟机栈中各栈帧的局部变量和参数:每个线程当前正在执行的方法里,所有未销毁的引用变量
- 方法区中的静态字段(类变量):如 public static List<String> cache; 所持有的引用
- 方法区中的常量引用:字符串常量池(StringTable)中对字符串实例的引用
- 本地方法栈中 JNI 引用:Java 调用 C/C++ 代码时,由本地代码持有的 Java 对象引用
- 正在运行的 Java 线程对象本身:线程实例及其内部状态(如阻塞队列中的锁对象引用)
分代收集对 GC Roots 的影响
GC Roots 不是固定不变的,它会随收集范围动态扩展:
- Young GC(Minor GC)只清理年轻代,但老年代中指向年轻代的引用也必须纳入 Roots——否则年轻代里被老年代“跨代引用”的对象会被误判为垃圾
- 某些结构(如 SystemDictionary、StringTable)在 Young GC 中被视为强根,但在 Full GC 中不参与强可达判断,因其语义上支持弱一致性
- 也就是说:收集范围越小,GC Roots 集合往往越大,这是为了保证部分收集的准确性
为什么不用引用计数?根集合如何解决循环引用
引用计数法无法处理 A↔B 循环引用导致计数永不归零的问题。而基于 GC Roots 的可达性分析天然规避了这一点:
- 只要 A 和 B 都没有被任何 GC Root 引用,无论它们怎么互相引用,整条子图都不可达
- 判断依据始终是“能否从外部活跃入口触达”,而非“内部是否自洽”
- 这使得 JVM 能精确识别真正无用的对象,而不受对象间关系拓扑干扰

















