finalize方法由jvm隐式触发,经可达性分析标记不可达对象后注册进unfinalized链表,由finalizerthread执行;对象可在finalize中复活一次,但java 9起已废弃该机制。

finalize 方法不是由开发者直接调用的,而是由 JVM 在对象回收流程中按特定机制隐式触发的。它不保证执行,也不可控,整个过程依赖 GC 判定、Finalizer 队列和专用线程协同完成。
对象先被判定为不可达
JVM 通过可达性分析(从 GC Roots 出发遍历引用链)判断对象是否还被任何活跃线程访问。一旦确认对象不可达,且该对象的类重写了 finalize() 方法,JVM 就会将其标记为“待终结”对象,而不是立即回收。
对象被注册进 Finalizer 链表
被标记的对象会被封装成一个 java.lang.ref.Finalizer 实例,并插入到 JVM 内部维护的 unfinalized 静态链表 中。这个链表是 package-private 的,开发者无法访问或干预,仅由 JVM 和 ReferenceHandler 线程管理。
- Finalizer 实例持有目标对象的弱引用,防止对象在 finalize 执行前被提前回收
- 每个 Finalizer 实例通过 next/prev 指针构成双向链表,便于高效注册与移除
- 注册完成后,对象就进入“等待执行 finalize”状态,但尚未真正死亡
FinalizerThread 主动拉取并执行
JVM 启动时会创建一个低优先级的守护线程 —— FinalizerThread。它持续轮询内部的 ReferenceQueue(实际是从 unfinalized 链表转移而来),取出待处理的 Finalizer 实例,然后调用其封装对象的 finalize() 方法。
- 该线程独立于 GC 线程和应用主线程,避免阻塞回收主流程
- 若 finalize() 抛出未捕获异常,JVM 会静默吞掉,不影响队列中其他对象的处理
- 执行完后,Finalizer 实例从链表中移除,对象才真正进入可回收状态
对象可能“复活”,但仅限一次
在 finalize() 方法体内,如果代码重新让该对象被某个活跃引用持有了(比如赋值给静态变量),对象就“复活”了——它脱离了回收流程,继续存活。但 JVM 保证每个对象的 finalize() 最多只执行一次:下次再不可达时,不会再次入队,而是直接回收。
这种机制曾被误用作资源兜底,但因不确定性高、性能开销大、易引发内存泄漏,Java 9 起已明确废弃 finalize(),推荐改用 Cleaner 或 PhantomReference + 句柄清理模式。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











