Java 面试题
目录

Java 虚拟机 (JVM) 中如何判断一个对象是否可以被回收?

· 约 3 分钟读完 · 第 121 / 274 章

推荐答案

在 Java 虚拟机 (JVM) 中,判断一个对象是否可以被回收主要通过可达性分析算法来实现。具体步骤如下:

  1. GC Roots:JVM 会从一组称为 "GC Roots" 的根对象开始,这些根对象包括:

    • 虚拟机栈(栈帧中的局部变量表)中引用的对象。
    • 方法区中类静态属性引用的对象。
    • 方法区中常量引用的对象。
    • 本地方法栈中 JNI(即 Native 方法)引用的对象。
  2. 可达性分析:从这些 GC Roots 开始,JVM 会遍历所有引用链,标记所有能够被 GC Roots 直接或间接引用的对象为存活对象。

  3. 不可达对象:如果一个对象无法通过任何引用链与 GC Roots 相连,那么这个对象就被认为是不可达的,即可以被回收。

  4. Finalization:在回收之前,JVM 会调用对象的 finalize() 方法(如果对象重写了该方法),给对象最后一次自救的机会。如果对象在 finalize() 方法中重新与 GC Roots 建立了引用链,那么它就不会被回收。

本题详细解读

1. GC Roots 的作用

GC Roots 是可达性分析的起点,它们是 JVM 中不会被回收的对象。通过 GC Roots,JVM 可以确定哪些对象是存活的,哪些对象是不可达的。GC Roots 包括以下几类:

  • 虚拟机栈中的局部变量:当前正在执行的方法中的局部变量引用的对象。
  • 方法区中的静态变量:类的静态属性引用的对象。
  • 方法区中的常量:字符串常量池中的引用、final 常量等。
  • 本地方法栈中的 JNI 引用:通过 JNI 调用的 Native 方法引用的对象。

2. 可达性分析的过程

可达性分析是从 GC Roots 开始,通过引用链遍历所有对象的过程。具体步骤如下:

  • 标记阶段:从 GC Roots 开始,遍历所有引用链,标记所有能够被 GC Roots 直接或间接引用的对象为存活对象。
  • 清除阶段:未被标记的对象被认为是不可达的,可以被回收。

3. Finalization 机制

在对象被回收之前,JVM 会调用对象的 finalize() 方法(如果对象重写了该方法)。finalize() 方法为对象提供了一个自救的机会。如果对象在 finalize() 方法中重新与 GC Roots 建立了引用链,那么它就不会被回收。不过,finalize() 方法并不保证一定会被执行,因此不应该依赖它来释放资源。

4. 其他回收机制

除了可达性分析,JVM 还使用了其他机制来辅助垃圾回收,例如:

  • 引用计数:虽然 Java 不主要使用引用计数,但在某些情况下,JVM 会使用引用计数来辅助判断对象是否可以被回收。
  • 分代收集:JVM 将堆内存分为新生代和老年代,不同代使用不同的垃圾回收算法。新生代通常使用复制算法,而老年代使用标记-清除或标记-整理算法。

通过这些机制,JVM 能够高效地管理内存,确保不再使用的对象能够被及时回收,从而避免内存泄漏和内存溢出等问题。