java 中 finalize 方法已被彻底废弃,因其设计上不可靠、不安全、不高效;应使用 try-with-resources 或 cleaner 替代,严禁重写或依赖 finalize。

Java 中 finalize 方法已被彻底废弃,不是因为它“用得少”,而是它从设计上就不可靠、不安全、不高效——它不能作为资源释放的可行方案,也不该出现在任何现代 Java 代码中。
finalize 的根本问题:不可控、不安全、拖性能
它不是“可能不执行”,而是 JVM 根本不承诺执行;不是“偶尔慢”,而是会让对象回收延迟数倍甚至引发 OOM:
- GC 不保证调用
finalize,也不保证调用时机或线程。ZGC、Shenandoah 等现代收集器可直接跳过终结流程 - 只要类定义了非空
finalize,所有实例都会被标记为“需终结”,必须经历至少两次 GC 才能真正回收 - Finalizer 线程是单线程、低优先级、无超时机制。一个阻塞(如 IO 等待)就会卡住整个队列,导致后续对象积压、老年代持续膨胀
- 允许“对象复活”——在
finalize内把this赋给静态变量,会破坏可达性分析,造成重复清理、状态错乱、内存泄漏 - 未捕获异常会被静默吞掉,子类重写时遗漏
super.finalize()就等于丢掉父类清理逻辑
替代方案一:显式关闭 + try-with-resources(首选)
适用于实现了 AutoCloseable 的资源,比如文件流、数据库连接、网络套接字等。这是最确定、最直观、最易测试的方式:
- 作用域结束即释放,无需依赖 GC
- 编译期检查是否遗漏关闭,IDE 和静态分析工具可辅助发现
- 支持嵌套资源自动关闭,语法简洁清晰
示例:
BufferedInputStream bis = new BufferedInputStream(fis)) {
// 使用资源
} // 自动调用 close()
替代方案二:Cleaner(处理本地资源)
适用于无法实现 AutoCloseable 的场景,比如 JNI 分配的 native 内存、DirectByteBuffer、文件映射等。Cleaner 基于虚引用,异步执行、不阻塞 GC、天然防复活:
- 通过
Cleaner.register(obj, runnable)关联对象与清理动作 - 清理任务在独立守护线程中运行,失败也不会影响 GC
- 支持手动调用
cleanable.clean()提前释放,适合 close() 方法内部使用 - 清理逻辑无法访问原对象(只传入所需参数),从根本上杜绝对象复活
示例关键结构:
private static final Cleaner cleaner = Cleaner.create();private final Cleanable cleanable;
public MyResource() {
this.handle = allocateNative();
this.cleanable = cleaner.register(this, () -> freeNative(handle));
}
public void close() {
cleanable.clean();
}
绝对不要做的事
这些行为在 JDK 9 后已被明确否定,且在 JDK 21 中底层支持已大幅移除:
- 重写
finalize(),哪怕只是空方法(仍会触发 Finalizer 队列开销) - 把它当作“析构函数”或“最后兜底”,用于关键资源释放
- 在
finalize中做日志、监控、同步操作或任何可能抛异常的逻辑 - 依赖它来弥补忘记调用
close()的疏漏
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











