Java 垃圾回收中 finalize 方法怎么执行
finalize 方法的执行依赖于 jvm 的 finalizer 守护线程,该线程会轮询一个由 gc 触发的待处理队列。当对象被判定为不可达且拥有 finalize 方法时,gc 会将其注册到该队列,随后由守护线程取出并调用其 finalize() 方法。如果该方法抛出异常,jvm 会记录日志但不会中断其他对象的清理工作。

对象如何进入 Finalizer 队列
对象进入 Finalizer 队列发生在标记-清除或复制算法的存活分析阶段之后。当 HotSpot 虚拟机发现一个对象不再被任何根引用可达,并且该对象继承或重写了 java.lang.Object 的 finalize() 方法时,它不会立即销毁该对象,而是将其放入一个特殊的内存区域或直接链接到一个内部队列中。

public class DemoFinalize {
@Override
protected void finalize() throws Throwable {
System.out.println("Object is being finalized");
super.finalize();
}
public static void main(String[] args) {
DemoFinalize obj = new DemoFinalize();
obj = null;
System.gc();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}Finalizer 守护线程的工作机制
Finalizer 守护线程(Daemon Thread)在 JVM 启动时创建,其优先级较低。它通过一个无限循环不断从 ReferenceQueue 中获取即将被终结的对象引用。一旦获取到引用,线程就会调用该对象的 runFinalizer() 方法,进而触发 finalize() 的执行。这个过程是异步的,因此无法保证 finalize() 会在某个特定时间点之前执行。
立即学习“Java免费学习笔记(深入)”;
public class FinalizerThreadDemo {
public static void main(String[] args) {
// 模拟大量对象触发 GC
for (int i = 0; i < 100; i++) {
new Object() {
@Override
protected void finalize() throws Throwable {
System.out.println("Finalizing object: " + this.hashCode());
super.finalize();
}
};
}
System.gc();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}基于程序中某个输入、条件或结果的具体问题
在上面的代码中,如果 finalize() 方法内部抛出了未捕获的异常,会发生什么?根据 JVM 规范,finalize() 中抛出的异常会被忽略,并打印到系统错误流中,而不会导致程序崩溃。这意味着即使清理逻辑失败,JVM 也会继续处理队列中的下一个对象。这种设计虽然保证了系统的稳定性,但也可能导致资源泄漏,因为如果 finalize() 在执行一半时出错,后续的资源释放代码将不会执行。


















