finalize 方法已被禁用,因其执行不可控、干扰 GC、易致死锁与内存泄漏;JDK 9 弃用,JDK 21 大幅移除;推荐使用 try-with-resources 或 Cleaner 替代。

因为 finalize 方法从设计到运行都不可控,既无法保证执行,又严重干扰垃圾回收,还容易引发死锁、内存泄漏和对象复活等严重问题。Java 官方早在 JDK 9 就明确弃用,JDK 18 起标注为 forRemoval = true,JDK 21 中已大幅移除底层支持。
执行完全不可预测,连“是否调用”都无法保障
GC 不承诺调用 finalize(),哪怕对象早已不可达;也不承诺调用时机——可能数小时不触发,也可能在 GC 压力大时集中爆发。JVM 可因优化策略、内存压力、甚至调试模式跳过调用。开发者写完代码后,根本不知道那段清理逻辑到底有没有跑过。
- 没有超时机制,Finalizer 线程卡住(比如 IO 阻塞或死锁),整个队列就停摆
- 未捕获异常会静默终止 Finalizer 线程,后续所有对象的 finalize 永远不再执行
- 子类重写时若遗漏 super.finalize(),父类资源清理逻辑直接丢失
拖慢 GC 效率,成倍增加内存压力
带 finalize 的对象不能被快速回收:必须先标记为“待终结”,入 Finalizer 队列,等专用线程串行处理完毕,才能进入下一轮 GC 判定。这导致对象生命周期延长数倍,GC 停顿变长,老年代占用持续攀升。
- 实测性能下降达 40–50 倍,尤其在高吞吐场景下尤为明显
- jstat -finalstats 显示 pending-finalize 队列长度不断增长,是 OOM 前典型征兆
- JVM 堆未满但频繁 Full GC,往往就是 finalize 积压所致
破坏对象可达性语义,埋下“对象复活”隐患
在 finalize() 内把 this 赋给静态变量或全局集合,对象就重新获得强引用——它逃过本次回收,下次 GC 又进队列、再被 finalize、再复活……形成恶性循环。
- 同一对象可能被多次 finalize(JVM 不保证只调一次)
- 资源重复关闭、状态错乱、缓存无限膨胀,都是常见后果
- Cleaner 等现代方案天然隔离清理动作与原对象,彻底杜绝复活可能
替代方案成熟可靠,无需妥协
官方早已提供更安全、可监控、低开销的替代路径:
- 显式管理:用 try-with-resources(要求 AutoCloseable)确保资源用完即关
- Cleaner(JDK 9+):基于虚引用实现,清理动作由独立线程异步执行,不阻塞 GC,不依赖对象状态
- PhantomReference + ReferenceQueue:适合框架层精细控制,但 Cleaner 已将其封装简化
finalize 不是“慎用”,而是“禁用”。它不是遗留功能,而是已被判定为危险且过时的设计缺陷。


















