在 Java 中,GC Roots(垃圾回收根对象)是垃圾回收器在执行垃圾回收时作为根节点的一组特殊对象。任何能够通过 GC Roots 直接或间接引用的对象都不会被垃圾回收,GC Roots 是垃圾回收算法的基础之一。 所以说呢,对于我们java程序员,了解哪些对象可以作为 GC Roots 对象,对 Java 程序的性能优化非常重要。
常见的 GC Roots 类型
以下是 Java 中常见的 GC Roots:
-
栈中的局部变量(方法调用栈中的变量)
- 位于各个线程的栈帧中的局部变量(如基本类型变量、对象引用)是 GC Roots,因为这些对象在方法执行时一直存活。例如:
public void someMethod() { Object obj = new Object(); // 局部变量 } - 这些局部变量在方法调用期间被保留,它们引用的对象不会被垃圾回收。
- 位于各个线程的栈帧中的局部变量(如基本类型变量、对象引用)是 GC Roots,因为这些对象在方法执行时一直存活。例如:
-
活跃的线程对象
- 所有正在运行的线程对象不会被垃圾回收。它们通常存储在堆上,但被 JVM 认为是“活跃”的,因此会被当作 GC Roots。例如,
Thread类的实例是活跃线程的引用。
- 所有正在运行的线程对象不会被垃圾回收。它们通常存储在堆上,但被 JVM 认为是“活跃”的,因此会被当作 GC Roots。例如,
-
类的静态字段
- 所有类加载器中加载的类的静态引用字段也是 GC Roots。类的静态字段会引用堆中的对象,这些对象不会被回收。
public class SomeClass { public static Object staticObject = new Object(); // 静态字段 } - 上面的例子中,
staticObject被引用的对象不会被回收。
- 所有类加载器中加载的类的静态引用字段也是 GC Roots。类的静态字段会引用堆中的对象,这些对象不会被回收。
-
JNI(Java Native Interface)引用
- 通过 JNI 引用的对象,或直接由 JVM 或 native 代码持有的引用对象,也是 GC Roots。例如,Java 与 C/C++ 代码进行交互时,native 代码中持有的 Java 对象不会被回收。
-
类加载器
- 所有正在使用的类加载器也是 GC Roots,因为它们持有对类及其静态字段的引用。类加载器(如
Bootstrap ClassLoader、System ClassLoader)会被作为垃圾回收的根节点。
- 所有正在使用的类加载器也是 GC Roots,因为它们持有对类及其静态字段的引用。类加载器(如
-
Java 内存管理中的管理对象
- 一些 JVM 管理的特殊对象(如常量池中的常量)会被作为 GC Roots。例如,
String.intern()生成的字符串对象会被 JVM 特殊对待。
- 一些 JVM 管理的特殊对象(如常量池中的常量)会被作为 GC Roots。例如,
-
同步锁(Monitor)持有的对象
- 当一个对象被锁定(如通过
synchronized关键字),这个对象也被认为是 GC Roots,因为它在同步块或方法执行期间一直存活。
- 当一个对象被锁定(如通过
-
软引用、弱引用、虚引用
- 这些特殊引用类型在特定的垃圾回收阶段也会被认为是 GC Roots。不同的引用类型对对象回收的强度不同。
其他常见的 JVM GC Roots
- Finalizer 队列中的对象:这些对象需要在被回收前执行
finalize()方法。 - Java 反射中的方法区:包含类元数据、常量池等信息,使用中会被作为 GC Roots。
- 系统类:如
System.out和System.err,这些对象都是常驻内存的,因而被视作 GC Roots。
总结
GC Roots 是在 Java 垃圾回收算法中作为根的对象,它们保证在垃圾回收的过程中这些对象不会被回收。常见的 GC Roots 包括:
- 方法栈中的局部变量
- 活跃的线程
- 静态字段
- JNI 代码引用的对象
- 类加载器
- JVM 内部管理对象等
这些 GC Roots 的存在保证了它们引用的对象不会被 GC 线程回收,除非 GC Roots 本身不再有效。