基于本文回答
0
评论

在 Java 中,哪些对象可以作为 GC Roots?

知识点图片

在 Java 中,垃圾回收器(Garbage Collector)使用可达性分析算法(Reachability Analysis)来判断对象是否存活。这个算法的基本思想是:通过一系列被称为 "GC Roots"(垃圾回收根节点) 的对象作为起点,向下搜索,如果一个对象到 GC Roots 之间没有任何引用链相连(即不可达),那么这个对象就被认为是垃圾,可以被回收。

在 Java(特别是主流的 HotSpot 虚拟机)中,可以作为 GC Roots 的对象主要包括以下几大类:

1. 核心的四大类(面试最常考)

  • 虚拟机栈(栈帧中的局部变量表)中引用的对象
    • 解释:各个线程正在执行的方法里的局部变量、参数等。
    • 举例:你在某个方法里写了 Object obj = new Object();,只要这个方法还没执行完,obj 变量对应的对象就是 GC Root 之一。
  • 方法区中类静态属性引用的对象
    • 解释:Java 类的引用类型静态变量。
    • 举例public static MyClass myClass = new MyClass();,这个 myClass 引用的对象就是 GC Root。只要该类不被卸载,它引用的对象就不会被回收。
  • 方法区中常量引用的对象
    • 解释:比如字符串常量池(String Table)里的引用,或者被 final 修饰的引用类型常量。
    • 举例public static final String NAME = "Java";
  • 本地方法栈中 JNI(即 Native 方法)引用的对象
    • 解释:Java 调用 C/C++ 代码时,C/C++ 代码中持有的对 Java 对象的引用。

2. JVM 内部和系统级引用

除了上面常见的四种,JVM 内部还有一些长期存活的对象也被作为 GC Roots:

  • Java 虚拟机内部的引用
    • 基本数据类型对应的 Class 对象(如 int.class, boolean.class)。
    • 一些常驻的异常对象(如 NullPointerException, OutOfMemoryError)。
    • 系统类加载器(System Class Loader)。
  • 所有被同步锁(synchronized 关键字)持有的对象
    • 解释:如果一个对象正在被用作锁,它绝对不能被回收,否则会破坏同步机制。
  • 反映 Java 虚拟机内部情况的 JMXBean、JVMTI 中注册的回调、本地代码缓存等
    • 解释:用于 JVM 监控、调试的内部数据结构引用的对象。
  • 活动线程(Active Threads)
    • 解释:所有正在运行的 Thread 对象本身。

3. 临时加入的 GC Roots(跨代/跨区引用)

在现代垃圾回收器(如 G1、ZGC 以及传统的分代 GC)中,由于垃圾回收经常是局部回收(Partial GC),还会存在“临时 GC Roots”:

  • 跨代引用(比如 Young GC 时的老年代对象)
    • 解释:当只回收新生代(Young GC)时,如果有老年代的对象引用了新生代的对象,为了避免扫描整个老年代,JVM 会将这些包含跨代引用的老年代对象(通过卡表 Card Table 记录)临时加入到 GC Roots 的集合中。

💡 总结与记忆技巧

为了方便记忆,你可以按照内存区域来划分:

  1. 线程私有区域(活着的): 虚拟机栈(Java方法局部变量)、本地方法栈(Native方法变量)、运行中的线程。
  2. 线程共享区域(全局的): 方法区(静态变量、常量)。
  3. JVM 底层必备(内部的): 锁对象、JVM内部类、系统类加载器。

核心判断逻辑:只要是当前正在被直接使用(栈中的变量)、全局共享且生命周期长(静态/常量)、或者JVM运行不可或缺的对象,就是 GC Roots。

右滑查看面试常问