finalize()方法允许对象在回收前通过F-Queue队列和重写该方法实现一次“自救”,但因执行时机不确定、仅限一次且性能差,Java 9起已被标记为@Deprecated,推荐使用Cleaner或PhantomReference替代。

finalize() 方法确实能让对象在被回收前“活过来”,但这种“起死回生”不是随意发生的,它依赖 JVM 的特定标记与队列机制,且只有一次机会。
对象自救发生的前提条件
不是所有对象都能自救。必须同时满足三个条件:
- 该对象已不可达(无法从任何 GC Root 追溯到)
- 它重写了 finalize() 方法(即不是 Object 默认实现)
- 它的 finalize() 方法尚未被 JVM 调用过
F-Queue 队列是自救的关键中转站
JVM 不会立刻回收满足上述条件的对象,而是先把它放入一个叫 F-Queue(Finalization Queue)的特殊队列。这个队列由低优先级的 Finalizer 线程异步处理——它逐个调用其中对象的 finalize() 方法。
重点在于:在 finalize() 方法内部,只要对象通过任意方式重新建立强引用链(比如把自己赋值给某个静态变量、或存入集合、或作为其他活跃对象的字段),它就再次变成“可达”,从而在后续的二次标记中被移出待回收集合。
为什么只能自救一次?
JVM 明确规定:每个对象的 finalize() 方法最多自动执行一次。一旦调用过,即使之后对象再次变得不可达,JVM 也不会再把它放进 F-Queue,也就彻底失去自救能力。
例如:SAVE_HOOK = this; 在第一次 finalize() 中成功复活;但第二次 GC 触发时,尽管 SAVE_HOOK 又被置为 null,JVM 已标记该对象“finalize 已执行”,直接跳过 F-Queue,进入最终回收流程。
自救不等于推荐使用
虽然机制存在,但实际开发中应避免依赖它:
- finalize() 执行时机不确定,可能永远不执行(比如程序中途退出)
- Finalizer 线程优先级低,容易造成延迟,甚至阻塞整个回收队列
- 若 finalize() 内部有死循环或耗时操作,会导致 F-Queue 积压,拖垮 GC 性能
- Java 9 开始已将 finalize() 标记为 @Deprecated,官方推荐用 Cleaner 或 PhantomReference 替代

















