finalize被废弃是因为其执行不可靠、线程不安全、性能极差且可能完全不调用;应改用cleaner或显式资源管理(如try-with-resources)。

finalize 方法已被现代 JDK 官方明确废弃并逐步停用,核心原因不是它“不好写”,而是它从根本上违背了资源管理的可靠性、可预测性和性能要求。它不是备用方案,而是系统性风险源。
执行完全不可控,无法用于关键逻辑
垃圾回收器不保证调用 finalize,也不保证调用时机或线程。对象可能在程序整个生命周期内都不被回收,finalize 就永远不会执行;也可能在 GC 压力大时集中触发,造成突发延迟。JVM 甚至可在某些配置下(如启用 -XX:+DisableExplicitGC 或使用 ZGC/Shenandoah 等低延迟收集器)直接跳过 finalization 步骤。这意味着:依赖它释放文件句柄、数据库连接或本地内存,等于把系统稳定性交给随机性。
严重拖慢垃圾回收,引发连锁故障
只要类重写了非空的 finalize 方法,该类所有实例都会被 JVM 特殊标记,进入 Finalizer 队列,必须经历至少两次 GC 才能真正回收:
- 第一次:标记为待终结,放入队列,对象仍占内存
- Finalizer 线程(单线程、低优先级、无超时)串行执行每个对象的 finalize()
- 第二次:确认未“复活”,才真正回收
这导致回收延迟数十倍,队列一旦积压(例如某个 finalize 里发生 IO 阻塞或死锁),后续所有待终结对象全部卡住,老年代持续膨胀,最终触发 OOM——而此时堆内存使用率可能远未达阈值。
语义危险且易误用,制造隐蔽缺陷
finalize 允许“对象复活”(即在方法体内将 this 赋给静态引用),直接破坏 GC 可达性判断基础。这不仅让同一对象可能被多次终结(JVM 不保证只调一次),还会导致资源重复关闭、状态错乱、缓存无限增长等难以复现的线上问题。更严重的是:子类重写时若忘记调用 super.finalize(),父类清理逻辑就彻底丢失;方法中抛出的任何异常都会被静默吞掉,连日志都看不到,排查时只剩 Finalizer 队列堆积这一条线索。
现代替代方案已成熟、标准、可控
官方早已提供更安全、更高效、更符合工程实践的替代路径:
- 显式资源管理:用 try-with-resources(要求实现 AutoCloseable),资源用完即关,时机确定、可监控、可加超时
- Cleaner API(Java 9+ 推荐):基于虚引用实现,注册清理动作时不持有强引用,不阻塞 GC,不支持对象复活,天然线程安全
- PhantomReference + ReferenceQueue:底层灵活,适合框架开发,但 Cleaner 已是其开箱即用封装
这些方式把资源生命周期交还给业务代码控制,而不是等待不可知的 GC 调度。JDK 自身(如 DirectByteBuffer、FileChannel)也早已全面迁移到 Cleaner。











