java 垃圾回收中的 finalize() 方法理论上可以拯救对象,但实际开发中严禁依赖此机制。若要在 finalize() 中保留对象,必须在方法体内重新建立该对象的强引用(例如将其赋值给静态变量或实例字段),这样在当次 gc 周期结束后,该对象将不再被视为垃圾,从而避免被回收。

finalize() 拯救对象的具体实现逻辑是什么
要实现对象拯救,核心在于打破“不可达”状态。当 JVM 判定一个对象为垃圾并准备执行 finalize() 时,如果在该方法内部将该对象的引用赋值给了某个存活对象(如静态字段),则该对象重新进入可达图。需要注意的是,这种机制仅对当前这一次 GC 有效,如果在下一次 GC 前没有再次处理,它仍会被回收。

public class FinalizeRescue {
// 用于保存被拯救对象的静态引用
private static FinalizeRescue savedInstance;
@Override
protected void finalize() throws Throwable {
try {
System.out.println("Finalize called, attempting to rescue...");
// 关键步骤:重新建立强引用
savedInstance = this;
} finally {
super.finalize();
}
}
public static void main(String[] args) {
// 创建对象
FinalizeRescue obj = new FinalizeRescue();
// 断开引用
obj = null;
// 建议手动触发 GC 以观察效果
System.gc();
// 等待 finalize 线程执行
try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
// 检查是否被拯救
if (savedInstance != null) {
System.out.println("Object rescued! Reference is: " + savedInstance);
} else {
System.out.println("Object was collected.");
}
}
}
运行上述代码,控制台通常会输出 “Finalize called...” 以及 “Object rescued! ...”。这证明了通过 savedInstance = this; 这一行代码,对象成功从垃圾回收器的队列中被拉回,恢复了生命力。
为什么现代 Java 不推荐使用 finalize() 拯救对象
尽管 finalize() 能拯救对象,但这种行为具有极大的不确定性和性能开销。JVM 并不保证 finalize() 一定会被执行,也不保证执行的顺序和时机。此外,拯救对象会导致 GC 暂停时间变长,因为需要遍历额外的引用链。在现代 Java 版本中,finalize() 已被标记为废弃(Deprecated),并最终在 Java 9+ 中引入了 java.lang.ref.Cleaner 作为替代方案,用于资源清理而非对象生命周期管理。

import java.lang.ref.Cleaner;
public class CleanerExample {
private static final Cleaner cleaner = Cleaner.create();
private boolean isClosed = false;
public CleanerExample() {
// 注册清理动作
cleaner.register(this, () -> {
System.out.println("Cleaning up resources...");
isClosed = true;
});
}
public void close() {
if (!isClosed) {
isClosed = true;
cleaner.clean(); // 主动触发清理
}
}
public static void main(String[] args) {
CleanerExample resource = new CleanerExample();
resource.close();
// 模拟对象变为不可达
resource = null;
System.gc();
try { Thread.sleep(1000); } catch (InterruptedException e) {}
}
}
使用 Cleaner 可以更可控地释放非堆内存资源,但它同样不能用于“拯救”对象的生命周期。开发者应通过显式的 close() 方法或 try-with-resources 语句来管理资源,而不是依赖 GC 的回调机制。
如果对象在 finalize() 中抛出异常会发生什么
如果在 finalize() 方法中抛出未捕获的异常,JVM 会忽略该异常,不会将其传播到主线程,同时该对象仍然会被正常回收,即使你试图在异常发生前拯救它也可能因栈展开而失败。这意味着 finalize() 中的错误处理必须非常谨慎,通常建议使用 try-catch 块包裹所有逻辑,确保即使出错也能尝试完成必要的引用重建或日志记录。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











