java 9起finalize()被标记废弃,java 18正式移除;推荐用基于虚引用的cleaner替代,它线程安全、异步执行、支持显式清理且不阻塞gc。

Java 9 起 Object.finalize() 被标记为废弃(@Deprecated),Java 18 正式移除;它不可靠、性能差、易引发内存泄漏和死锁,不应再使用。推荐用 Cleaner —— 基于虚引用(PhantomReference)的轻量、线程安全、可显式控制的资源清理机制。
理解 Cleaner 的核心设计
Cleaner 不依赖 GC 触发时机,而是将清理逻辑与对象生命周期解耦:当对象变得不可达时,JVM 会把关联的 Cleaner.Cleanable 入队,后台 cleaner 线程异步执行注册的清理动作。它不阻塞 GC,也不影响对象回收速度。
- 每个
Cleaner实例维护一个守护线程(默认命名Cleaner-0),监听引用队列 - 通过
Cleaner.register(obj, runnable)关联对象与清理任务 - 清理动作在独立线程中执行,避免阻塞 GC 线程或用户线程
- 支持手动调用
clean()提前触发清理(适合显式释放场景)
基本用法:替换 finalize 的典型模式
例如一个持有 native 内存或文件句柄的类,过去可能这样写:
// ❌ 已废弃,不要用
protected void finalize() throws Throwable {
freeNativeResource();
super.finalize();
}改用 Cleaner:
public class ResourceManager {
private static final Cleaner cleaner = Cleaner.create();
private final Cleanable cleanable;
private final long nativeHandle;
<pre class="brush:java;toolbar:false;">public ResourceManager() {
this.nativeHandle = allocateNativeResource();
// 注册清理动作:对象不可达时自动调用
this.cleanable = cleaner.register(this, () -> freeNativeResource(nativeHandle));
}
// 可选:提供显式关闭方法(更推荐)
public void close() {
cleanable.clean(); // 立即触发清理,避免等待 GC
}
private static long allocateNativeResource() { /* ... */ }
private static void freeNativeResource(long handle) { /* ... */ }}
关键注意事项和最佳实践
Cleaner 是工具,不是银弹。用不好仍会导致资源泄漏或竞态问题:
- 不要在清理动作中访问已不可达对象的字段:此时对象可能已被回收,字段值不确定;所有必要参数(如句柄、路径)应在注册时捕获闭包
-
优先使用显式资源管理(try-with-resources):Cleaner 是兜底机制,不能替代
AutoCloseable+close() - 避免在清理动作中抛出未捕获异常:会终止 cleaner 线程,后续清理全部失效;务必 try-catch 并记录日志
- 静态 Cleaner 实例通常足够:全局复用一个 cleaner 比每个类新建更高效;无需频繁创建
对比 finalize 的实际优势
为什么 Cleaner 更可靠:
- GC 不保证调用
finalize(),但会确保入队Cleanable(只要 cleaner 线程存活) -
finalize()在 GC 线程中同步执行,拖慢回收;Cleaner 异步执行,无干扰 -
finalize()只能执行一次且不可取消;Cleaner 支持多次clean()调用,且可主动注销 -
finalize()容易因异常导致对象“复活”;Cleaner 无此风险
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











