java终结器(finalize)自java 9起废弃、java 18默认禁用,因其执行时机不确定、不可靠且低效;应改用closeable、cleaner、try-with-resources等确定性资源管理机制。

Java 中终结器(finalize())已被标记为废弃(自 Java 9 起),且从 Java 18 开始默认禁用,不推荐用于资源清理或生命周期管理。垃圾回收器(GC)对终结器引用的处理是特殊但低效、不可靠的,其生命周期管理本质上是“尽力而为”,而非确定性保障。
终结器引用的 GC 处理流程
当对象重写了 finalize() 方法且未被调用过,JVM 会将该对象包装为一个 Finalizer 实例,并注册到一个内部的 FinalizerReference 链表中。GC 发现该对象仅剩终结器引用可达时:
- 不会立即回收对象,而是将其加入一个待终结队列(
ReferenceQueue) - JVM 启动一个低优先级守护线程(
Finalizer线程)轮询该队列,逐个调用finalize() -
finalize()执行完毕后,对象才真正变为可回收状态;若该方法抛异常或阻塞,会阻塞整个终结队列处理 - 若对象在
finalize()中“自救”(如将 this 赋值给某静态变量),它可能再次变为强可达——但仅限一次,后续 GC 不再触发finalize()
为什么终结器引用的生命周期不可控
终结器的执行时机、是否执行、执行顺序均无任何保证:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- GC 可能永远不运行(如内存充足),导致
finalize()永不调用 - 多个对象间的终结顺序不确定,无法依赖相互清理逻辑
- Finalizer 线程可能长期饥饿或被阻塞,造成大量对象堆积在队列中,引发内存泄漏或 OOM
- 子类和父类的
finalize()调用顺序不明确,易遗漏清理
替代方案:用现代 API 实现可靠资源管理
应完全避免使用 finalize(),改用以下明确、可预测的机制:
-
显式关闭(Closeable/AutoCloseable):实现
close()方法,配合 try-with-resources 自动调用 -
Cleaner(Java 9+):基于虚引用(
PhantomReference)和清理线程,不阻止对象回收,适合释放堆外资源(如文件句柄、内存映射) - try-finally 或 try-with-resources:确保关键清理逻辑在作用域退出前执行
- WeakReference/PhantomReference + ReferenceQueue:手动监听对象回收事件,由应用控制清理节奏(比 Finalizer 更轻量、更可控)
如果必须兼容旧代码:最小化风险
极少数遗留系统需维持 finalize(),务必遵守:
- 只做最简清理(如记录日志),绝不执行 I/O、加锁、等待或分配新对象
- 始终调用
super.finalize()(若继承链中有重写) - 在
finalize()内捕获所有异常,防止中断终结队列 - 主动调用
System.runFinalizersOnExit(true)已被移除,不可用;也不应依赖 JVM 退出时的批量终结
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










