finalize() 是 Object 的受保護方法,旨在對象被 GC 回收前提供「最後機會」釋放非內存資源(文件句柄、Socket、Native 內存)。但因執行時間不確定、線程不確定、異常吞沒、性能開銷大,已被 JDK 9 標記為棄用,JDK 18 移除 finalize 實現。本文詳解其機制、風隀與現代替代方案。
protected void finalize() throws Throwable {
try {
// 釋放非內存資源
if (fileHandle != null) closeFile();
} finally {
super.finalize(); // 必須調用父類 finalize
}
}
1. 對象變為不可達 2. GC 發現對象覆寫了 finalize() 且未被最終化 3. 對象移入「最終化隊列」 4. 低優先級 Finalizer 線程逐個調用 finalize() 5. finalize() 完成 → 對象再次變為不可達 6. 下輪 GC 真正回收內存
thisRef = this)也不會再次調用。Finalizer 線程執行,非業務線程。finalize() 拋出異常會被忽略,最終化過程終止,對象可能處於不一致狀態。finalize() 中睡眠/死鎖會導致所有對象最終化停滯,引發 OOM。class Zombie {
static Zombie ref;
@Override protected void finalize() {
ref = this; // 復活:重新變為可達
System.out.println("我復活了!");
}
}
public class ResurrectionDemo {
public static void main(String[] args) throws InterruptedException {
Zombie z = new Zombie();
z = null;
System.gc(); Thread.sleep(100);
System.out.println("第一次 GC 後: " + (Zombie.ref != null ? "存活" : "已死"));
Zombie.ref = null; // 再次不可達
System.gc(); Thread.sleep(100);
System.out.println("第二次 GC 後: " + (Zombie.ref != null ? "存活" : "已死")); // 不會再次 finalize
}
}
try-with-resources、Cleaner、PhantomReference 更安全、可控。| 方案 | 適用場景 | 核心 API | 優點 |
|---|---|---|---|
| try-with-resources (JDK 7+) | 作用域明確的資源 (流、連接、鎖、臨時文件) | AutoCloseable.close() | 確定性釋放、異常抑制、語法簡潔、編譯器輔助 |
| Cleaner (JDK 9+) | 無明確作用域、需依賴 GC 觸發的資源 (Native 內存、堆外緩衝區) | Cleaner.create(obj, cleaningAction) | 可控清理線程、不阻塞 GC、支援註冊/取消、無復活風險 |
| PhantomReference + ReferenceQueue | 極度底層控制、自定義清理調度 | ReferenceQueue/PhantomReference | 完全可控、可構建對象池、緩存淘汰框架 |
| Finalizer (舊) | 維護極舊代碼 (JDK 6/7) | protected void finalize() | 無優勢,僅為兼容 |