GC Roots包括:1. 当前栈帧中未出作用域且非null的局部变量;2. 已初始化且未卸载类的静态字段;3. 运行时常量池中驻留的字符串等常量对象;4. JNI全局引用、同步块内锁对象及活跃线程对象。
可达性分析中能作为gc roots的变量,不是“所有被引用的变量”都算,而是有明确边界和运行时语义约束的。关键不在于变量本身是否是引用类型,而在于它是否处于程序执行不可中断的活跃上下文中,且其指向的对象必须存活才能保障jvm当前行为正确。
栈帧里的本地变量表项才真正算根
虚拟机栈中,只有当前正在执行的方法栈帧中本地变量表里存储的引用,才构成GC Roots。不是所有局部变量,而是仍处于作用域内、尚未出栈、且值非null的引用变量。
- 方法刚进入,
Object obj = new Object();→obj是GC Root - 方法执行到末尾,
return;前,该栈帧仍在,obj仍是GC Root - 方法返回后,栈帧弹出,
obj变量消失,对象立即失去GC Roots身份(即使没被回收) - 若变量被显式赋值为
null,如obj = null;,则该引用不再指向任何对象,也不再贡献GC Roots
静态字段必须属于已初始化且未卸载的类
方法区中的静态属性(static 字段)能否成为GC Roots,取决于其所属类的状态:
- 类已完成初始化(
<clinit>执行完毕),且未被卸载 → 其static Object ref是GC Root - 类加载失败或初始化异常中止 → 静态字段未真正建立,不构成GC Roots
- 使用自定义类加载器加载的类,若加载器自身被回收、且该类无其他强引用 → 类可被卸载,其静态字段随之失效
-
static final String s = "abc";中的字符串常量池对象,因常量池本身受JVM保护,通常长期有效
常量引用仅限运行时常量池中真实驻留的项
不是所有字面量或final变量都自动进常量池,只有满足编译期常量条件、且被实际放入运行时常量池的引用,才算GC Roots:
-
String s1 = "hello";→ “hello”在字符串常量池中,是GC Root -
String s2 = new String("hello");→ 堆中新对象,不是常量池项,不构成GC Root(除非另有引用) -
static final Integer X = 127;→ 小整数可能触发Integer缓存,但缓存对象本身由JVM内部持有,不是用户代码的“变量”直接担当Root - 运行时常量池中符号引用解析后的类、方法、字段等元数据引用,也参与GC Roots判定(如Class对象)
JNI引用和锁对象需满足即时有效性
本地方法栈中的JNI全局/局部引用,以及synchronized锁住的对象,其GC Roots身份是瞬时且受状态约束的:
- JNI全局引用(
NewGlobalRef):只要未调用DeleteGlobalRef,就始终是GC Root - JNI局部引用:默认随Native方法返回而失效;若调用
EnsureLocalCapacity或手动管理,需显式清除 -
synchronized(obj) { ... }:仅在同步块执行期间,obj是GC Root;一旦退出,即解除 - 线程对象本身(如
Thread.currentThread())也是GC Root,但仅当线程处于RUNNABLE、BLOCKED等活跃状态时成立

















