GcRoots底层剖析

260 阅读3分钟

在 Java 中,GC Roots(垃圾回收根对象)是垃圾回收器在执行垃圾回收时作为根节点的一组特殊对象。任何能够通过 GC Roots 直接或间接引用的对象都不会被垃圾回收,GC Roots 是垃圾回收算法的基础之一。 所以说呢,对于我们java程序员,了解哪些对象可以作为 GC Roots 对象,对 Java 程序的性能优化非常重要。

常见的 GC Roots 类型

以下是 Java 中常见的 GC Roots:

  1. 栈中的局部变量(方法调用栈中的变量)

    • 位于各个线程的栈帧中的局部变量(如基本类型变量、对象引用)是 GC Roots,因为这些对象在方法执行时一直存活。例如:
      public void someMethod() {
          Object obj = new Object(); // 局部变量
      }
      
    • 这些局部变量在方法调用期间被保留,它们引用的对象不会被垃圾回收。
  2. 活跃的线程对象

    • 所有正在运行的线程对象不会被垃圾回收。它们通常存储在堆上,但被 JVM 认为是“活跃”的,因此会被当作 GC Roots。例如,Thread 类的实例是活跃线程的引用。
  3. 类的静态字段

    • 所有类加载器中加载的类的静态引用字段也是 GC Roots。类的静态字段会引用堆中的对象,这些对象不会被回收。
      public class SomeClass {
          public static Object staticObject = new Object(); // 静态字段
      }
      
    • 上面的例子中,staticObject 被引用的对象不会被回收。
  4. JNI(Java Native Interface)引用

    • 通过 JNI 引用的对象,或直接由 JVM 或 native 代码持有的引用对象,也是 GC Roots。例如,Java 与 C/C++ 代码进行交互时,native 代码中持有的 Java 对象不会被回收。
  5. 类加载器

    • 所有正在使用的类加载器也是 GC Roots,因为它们持有对类及其静态字段的引用。类加载器(如 Bootstrap ClassLoaderSystem ClassLoader)会被作为垃圾回收的根节点。
  6. Java 内存管理中的管理对象

    • 一些 JVM 管理的特殊对象(如常量池中的常量)会被作为 GC Roots。例如,String.intern() 生成的字符串对象会被 JVM 特殊对待。
  7. 同步锁(Monitor)持有的对象

    • 当一个对象被锁定(如通过 synchronized 关键字),这个对象也被认为是 GC Roots,因为它在同步块或方法执行期间一直存活。
  8. 软引用、弱引用、虚引用

    • 这些特殊引用类型在特定的垃圾回收阶段也会被认为是 GC Roots。不同的引用类型对对象回收的强度不同。

其他常见的 JVM GC Roots

  • Finalizer 队列中的对象:这些对象需要在被回收前执行 finalize() 方法。
  • Java 反射中的方法区:包含类元数据、常量池等信息,使用中会被作为 GC Roots。
  • 系统类:如 System.outSystem.err,这些对象都是常驻内存的,因而被视作 GC Roots。

总结

GC Roots 是在 Java 垃圾回收算法中作为根的对象,它们保证在垃圾回收的过程中这些对象不会被回收。常见的 GC Roots 包括:

  • 方法栈中的局部变量
  • 活跃的线程
  • 静态字段
  • JNI 代码引用的对象
  • 类加载器
  • JVM 内部管理对象等

这些 GC Roots 的存在保证了它们引用的对象不会被 GC 线程回收,除非 GC Roots 本身不再有效。