在Java的GC(垃圾回收)机制中,存活对象的核心定义是“从GC Root出发,通过引用链可达的对象”。这是一种基于“可达性分析”的判断方式,相比早期的“引用计数法”(容易出现循环引用问题),能更准确地识别需要保留的对象。

一、先理解:什么是“存活对象”?

简单说:一个对象如果能被“GC Root”直接或间接引用到,就被视为“存活对象”;反之,若从所有GC Root出发,都无法通过引用链访问到该对象,就被视为“垃圾对象”(可被GC回收)。

可以类比成“城市供电系统”:

  • GC Root 相当于“发电厂”(能源源头,不会断电);
  • 引用链相当于“输电线路”;
  • 对象相当于“用电设备”;
  • 存活对象 = 能通过输电线路从发电厂获得电力的设备(必须保留);
  • 垃圾对象 = 所有输电线路都无法连接到发电厂的设备(可以拆除)。

二、关键:什么是GC Root?

GC Root(垃圾回收根节点)是一组“绝对不会被GC回收”的特殊对象,它们是可达性分析的“起点”。这些对象之所以不会被回收,是因为它们直接关联着程序的运行状态(如正在执行的方法、静态资源等)。

Java中常见的GC Root类型有以下4类:

1. 虚拟机栈(栈帧中的局部变量表)中引用的对象

虚拟机栈是线程运行时的内存区域,每个方法执行时会创建“栈帧”,其中的“局部变量表”存储着方法内的局部变量(如参数、临时变量)。这些局部变量引用的对象,是当前线程正在使用的,绝对不能被回收。

例子

public void test() {
    User user = new User(); // user是局部变量,存储在栈帧的局部变量表中
    // 此时,User对象被“user”引用,而“user”是GC Root(属于虚拟机栈中的引用)
    // 因此User对象是存活的
}

2. 方法区中类静态属性引用的对象

类的静态属性(static修饰的成员变量)属于类本身,生命周期与类一致(类卸载时才可能被回收),因此静态属性引用的对象是GC Root。

例子

public class UserService {
    // 静态属性引用的对象
    public static User currentUser = new User();
    // “currentUser”是类静态属性,属于GC Root,因此它引用的User对象是存活的
}

3. 方法区中常量引用的对象

方法区中的“常量”(如String常量池中的字符串、static final修饰的常量)一旦加载,通常不会被回收,因此它们引用的对象也是GC Root。

例子

public class Constants {
    // 常量引用的对象
    public static final User ADMIN = new User("admin");
    // “ADMIN”是常量,属于GC Root,因此它引用的User对象是存活的
}

4. 本地方法栈中JNI(Native方法)引用的对象

本地方法栈是执行Native方法(非Java语言实现的方法,如C/C++代码)的内存区域,其中引用的对象(如通过JNIEnv创建的对象)也是GC Root。

三、“从GC Root出发可达”的具体含义

“可达”指的是通过“引用链”能层层访问到。具体过程如下:

  1. 标记起点:GC开始时,先标记所有GC Root对象(这些对象一定存活);
  2. 遍历引用链:从每个GC Root出发,递归遍历它们引用的对象,被遍历到的对象标记为“存活”(因为能通过GC Root访问到);
  3. 识别垃圾:未被标记的对象(从所有GC Root出发都无法访问到)就是垃圾,等待被回收。

举例说明:

假设存在对象引用关系:栈帧局部变量a → 对象A → 对象B → 对象C,且a是GC Root(虚拟机栈中的引用)。

  • 遍历过程:从a(GC Root)出发,能访问到A(标记存活);从A出发能访问到B(标记存活);从B出发能访问到C(标记存活)。
  • 结果:A、B、C都是存活对象。

如果此时a不再引用A(如a = null),则引用链断裂:

  • 从所有GC Root出发,都无法访问到A、B、C;
  • 结果:A、B、C都被视为垃圾,会被GC回收。

四、为什么用“GC Root可达性”判断存活?

早期的“引用计数法”(每个对象记录被引用次数,次数为0则回收)存在致命缺陷:无法解决循环引用问题(如A引用B,B引用A,两者引用计数都不为0,但实际已无用)。

而“GC Root可达性分析”能完美解决循环引用:即使A和B互相引用,只要没有任何GC Root引用它们,就会被判定为垃圾。

总结

  • 存活对象:从任意一个GC Root出发,通过引用链能访问到的对象。
  • GC Root:一组不会被GC回收的“根对象”(如栈中局部变量、静态属性、常量、Native方法引用的对象),是可达性分析的起点。
  • 核心逻辑:GC通过“从GC Root出发遍历引用链”标记存活对象,未被标记的则作为垃圾回收——这是Java GC判断对象生死的核心机制。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐