可达性分析: 什么东西可以被当作根

发布于:2025-09-12 ⋅ 阅读:(14) ⋅ 点赞:(0)

可达性分析: 什么东西可以被当作根?

在JVM的垃圾回收(GC) 中,可达性分析(Reachability Analysis) 是判断对象是否“存活”的核心算法——通过判断对象是否能从“根节点(GC Roots)”出发被访问到,来决定是否回收该对象。

只有满足**“可作为GC Roots”条件**的对象,才会被标记为根节点。以下是JVM中典型的GC Roots来源,按类别清晰拆解:

一、虚拟机栈(栈帧中的本地变量表)

虚拟机栈(每个线程私有)中,当前正在执行的方法的栈帧(Stack Frame)里的本地变量表,存储的引用类型变量所指向的对象,会被设为GC Roots。

  • 原理:这些变量是当前线程正在直接使用的对象(比如方法参数、局部变量),如果回收它们,会导致程序运行出错。
  • 示例
    public void test() {
        // obj是当前方法栈帧本地变量表中的引用,指向的对象会被设为GC Roots
        Object obj = new Object(); 
        System.out.println(obj); // 执行过程中,obj引用的对象始终是GC Roots
    }
    
  • 注意:当方法执行结束(栈帧出栈),该变量的引用消失,对应的对象就不再是GC Roots的一部分。

二、方法区中的静态变量(类静态属性)

方法区(元空间/永久代)中,类的静态引用类型变量所指向的对象,会被设为GC Roots。

  • 原理:静态变量属于“类级别的属性”,生命周期与类一致(类不被卸载,静态变量就一直存在),且被所有对象共享,必须作为根节点避免误回收。
  • 示例
    public class MyClass {
        // 静态变量staticObj引用的对象,会被设为GC Roots(只要MyClass不被卸载)
        public static Object staticObj = new Object(); 
    }
    
  • 注意:若类被JVM卸载(如自定义类加载器加载的类),则其静态变量对应的对象会失去GC Roots身份。

三、方法区中的常量(运行时常量池)

方法区的运行时常量池中,常量类型的引用所指向的对象,会被设为GC Roots。

  • 原理:常量(如String常量池中的字符串引用、final修饰的常量)在程序运行期间通常不会被修改,且可能被多处引用,必须作为根节点。
  • 示例
    public class MyClass {
        // 常量CONST_OBJ引用的对象,会被设为GC Roots
        public static final Object CONST_OBJ = new Object(); 
    }
    
  • 典型场景String s = "hello"; 中,“hello”是字符串常量池中的常量,其引用指向的对象会被设为GC Roots。

四、本地方法栈中的JNI引用(Native方法引用)

本地方法栈(存储Native方法执行时的栈帧)中,Native方法通过JNI(Java Native Interface)调用时使用的引用类型变量(如jobjectjclass等),所指向的对象会被设为GC Roots。

  • 原理:Native方法(如调用C/C++实现的方法)是JVM外部代码,其引用的Java对象无法通过Java层面的栈/方法区追踪,因此必须作为根节点,防止被误回收。
  • 示例:若Java代码调用一个Native方法native void process(Object data);,则data参数在Native方法执行期间,其指向的对象会被设为GC Roots。

五、活跃的线程对象

JVM中正在运行的线程对象(Thread实例) 本身,会被设为GC Roots。

  • 原理:线程是程序执行的基本单位,只要线程还在运行(未终止),其关联的资源(如栈帧、本地变量)都需要保留,因此线程对象本身必须是根节点。
  • 注意:即使线程处于阻塞状态(如sleep()wait()),只要未终止,仍会被视为GC Roots。

六、JVM内部的引用(系统级引用)

JVM自身运行过程中需要依赖的一些“系统级对象”,其引用也会被设为GC Roots,包括:

  • 垃圾回收器自身的引用(如GC标记过程中使用的临时对象);
  • JVM的类加载器(如启动类加载器Bootstrap ClassLoader);
  • 系统类(如java.lang.Objectjava.lang.Class等核心类)的引用;
  • JVM内部的线程(如Finalizer线程,负责执行对象的finalize()方法)。

这些对象是JVM正常运行的基础,绝对不能被回收,因此必须作为根节点。

总结:GC Roots的核心类别

根节点来源 具体对象/引用 核心原因
虚拟机栈 当前方法栈帧本地变量表中的引用 程序正在使用的对象,不可回收
方法区-静态变量 类的静态引用类型变量 生命周期与类一致,全局共享
方法区-常量 运行时常量池中的引用(如String常量) 常量不可修改,可能被多处引用
本地方法栈 Native方法的JNI引用(如jobject) 外部Native代码依赖的Java对象
活跃线程 正在运行/阻塞的Thread实例 线程未终止,关联资源需保留
JVM内部引用 GC自身引用、类加载器、系统类等 维持JVM正常运行的基础对象

理解GC Roots的关键:这些对象是“绝对不可能被回收”的,因为它们直接或间接被程序运行、JVM自身所依赖。可达性分析的本质,就是从这些根节点出发,遍历对象引用链,未被遍历到的对象就是“不可达”的,最终会被GC回收。


网站公告

今日签到

点亮在社区的每一天
去签到