首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java finalize() 深度解析:為什麼它不是 C++ 解構函數,以及正確的資源釋放模式

     編輯:Java 相關知識

前言

許多 C++ 開發者轉 Java 時,直覺將 finalize() 視為解構函數。本文透過 JVM 垃圾回收機制、執行時期行為實測,證明兩者本質差異,並建立正確的資源管理認知。

核心差異對比

特性C++ 解構函數Java finalize()
觸發時機離開作用域 (棧) / delete (堆) — 確定性GC 判定物件不可達時 — **非確定性**
執行順序子類 → 父類 (自動鏈式)**不自動呼叫父類**,需手動 super.finalize()
執行保證一定執行 (除非 terminate())**不保證執行** (JVM 關閉前可能不 GC)
例外處理例外導致程式終止例外被 GC 執行緒吞沒,不影響應用
主要用途釋放所有資源 (記憶體、檔案、鎖、Socket)僅作為**最後防線**釋放非 Java 資源 (JNI、Native 句柄)

垃圾回收器與 finalize() 互動機制

  1. 物件變為不可達 → 標記為可回收。
  2. GC 執行緒 (低優先級) 處理回收佇列。
  3. 若物件覆寫 finalize(),**第一次 GC 將物件移入 Finalizer 佇列**,由 Finalizer 執行緒稍後執行 finalize()。
  4. finalize() 執行完成 → 物件再次變為不可達 → **下一次 GC 才真正釋放記憶體**。
  5. 若 finalize() 中重新建立強引用 (物件復活),物件逃脫回收,但**第二次不可達時不再呼叫 finalize()**。

實證實驗:finalize() 不可靠

class Thing {
    static int created = 0, finalized = 0;
    Thing() { created++; }
    protected void finalize() { finalized++; }
}

public class FinalizeDemo {
    public static void main(String[] args) {
        while (Thing.finalized < 1) new Thing(); // 建立 1000+ 物件
        System.out.println(Thing.created + " created");
        System.out.println(Thing.finalized + " finalized");
        // 輸出:1000+ created, 0 finalized (程式結束前 GC 未執行)
    }
}

即使呼叫 System.gc() 也僅是**建議**,JVM 可能忽略。JDK 1.1 新增 System.runFinalizersOnExit(true) 強制退出時執行,但因執行順序不可控、效能影響大,**已於 JDK 1.2 棄用並移除**。

正確的資源釋放模式

1. Try-with-resources (JDK 7+) — 首選

try (FileInputStream in = new FileInputStream("data.txt");
     BufferedReader br = new BufferedReader(new InputStreamReader(in))) {
    String line = br.readLine();
    // 自動按相反順序 close(),即使拋出例外
}

2. Try-finally (舊版本相容)

FileInputStream in = null;
try {
    in = new FileInputStream("data.txt");
    // 業務邏輯
} finally {
    if (in != null) try { in.close(); } catch (IOException ignored) {}
}

3. AutoCloseable 自訂資源

public class NativeResource implements AutoCloseable {
    private long nativeHandle;
    public NativeResource() { nativeHandle = nativeAllocate(); }
    public void close() { if (nativeHandle != 0) nativeFree(nativeHandle); nativeHandle = 0; }
    protected void finalize() { close(); } // 最後防線
    private native long nativeAllocate();
    private native void nativeFree(long handle);
}

finalize() 仍有價值的極少數場景

  • JNI/Native 程式庫未正確實作 close(),且無法修改原始碼時,作為保底釋放 Native 句柄。
  • 偵測資源洩漏:在 finalize() 記錄堆疊追蹤,開發階段發現未正確關閉的資源。

結論

永遠不要依賴 finalize() 釋放關鍵資源。它是非確定性、不可靠、效能負擔的「最後防線」。請採用 try-with-resources 或 try-finally 明確管理資源生命週期,這才是 Java 風格的資源安全保證。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved