GC Roots是JVM严格规定的不可回收引用起点,包括虚拟机栈局部变量、方法区静态/常量引用、JNI全局引用;它们构成可达性分析唯一依据,错误持有易致内存泄漏。

Java 垃圾回收不是靠“标记重要性”来选 GC Roots,而是由 JVM 严格规定哪些引用在当前时刻必须存活、不能被回收——这些引用就是 GC Roots。它们是可达性分析的唯一起点,不是可选项,而是设计层面的硬性约定。
虚拟机栈中的局部变量引用
每个线程执行方法时,都会在栈中创建栈帧,其中局部变量表里保存着正在使用的对象引用。只要方法没返回,这些引用就有效。
- 例如:
String s = new String("abc");中的s是 GC Root,它指向的字符串实例因此不可回收 - 方法一退出,栈帧弹出,这个引用消失,对象立刻可能变为不可达
- 注意:引用本身(如变量
s)是 Root,它指向的对象是被保护的,不是 Root
方法区中静态字段引用的对象
类加载后,其 static 字段若持有非 null 对象,该对象就被视为 GC Root。
- 例如:
public static Map<String, Object> cache = new HashMap<>();中的HashMap实例是 GC Root - 只要类没卸载、字段没置为
null,这个引用就一直存在 - 这是内存泄漏高发场景:缓存长期持有大对象,却忘了清理
方法区常量池中的引用
编译期确定、运行期由 JVM 统一管理的常量,包括字符串字面量、public static final 基本类型包装类缓存等。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
-
"hello"这样的字符串字面量,在 JDK 7+ 后虽位于堆中,但仍是 GC Root(因为常量池条目直接引用它) -
Integer.valueOf(127)返回的缓存对象也是 GC Root;但valueOf(200)不是(超出缓存范围,每次新建) - 注意:
final static修饰的引用类型常量(如public static final List<?> EMPTY = Collections.emptyList();),其指向的对象也是 GC Root
本地方法栈中的 JNI 全局引用
Java 调用 native 方法时,C/C++ 代码可通过 NewGlobalRef 显式创建全局引用,这类引用会阻止对应 Java 对象被回收。
- 只有
GlobalRef是 GC Root;LocalRef随 JNI 方法返回自动失效,不构成强引用 - 如果 native 侧忘记调
NewGlobalRef,只保存了原始指针,GC 可能回收对象,后续访问导致崩溃 - JVM 内部也用类似机制持有关键对象(如系统类加载器),但它们本质仍归属上述四类的载体
不复杂但容易忽略:线程对象、Class 对象、synchronized 锁对象本身不是独立 Root 类型,而是因为被栈变量、静态字段或 JVM 内部结构持有,才间接成为可达链的锚点。判断依据永远是——此刻是否被 JVM 认定为不可回收的强引用起点。

















