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

Java 垃圾回收機制深度解析:特征、finalize 陷阱與內存洩漏實戰

     編輯:Java 綜合教學

GC 三大核心特征

  1. 僅管堆內存:不回收物理資源(DB 連線、檔案句柄、JNI malloc 記憶體)。
  2. 不可精確控制:只能建議(System.gc()、Runtime.getRuntime().gc()),JVM 自行決定是否執行。
  3. 回收前必調用 finalize():但調用時機同樣不確定,不可依賴其釋放關鍵資源。

三大應對策略

1. 手動清理非堆資源(try-finally 模式)

FileInputStream fis = null;
try {
    fis = new FileInputStream("./src/ManualClear.java");
    // 讀取操作...
} finally {
    if (fis != null) try { fis.close(); } catch (IOException ignored) {}
}

2. 消除過期引用,防止 OOM

三大常見洩漏場景:

  • 自管理記憶體類(如自實現 Stack):pop() 後須顯式 elements[size] = null;,否則數組持有引用阻礙 GC。
  • 緩存:長期持有對象引用。建議用 WeakHashMap 或後台線程定期清理過期項。
  • 監聽器/回調:註冊後未顯式註銷。需提供 removeListener() 並確保調用。

3. 謹慎使用 finalize()

三大合適場景:

  1. 安全網:手動清理遺漏時兜底釋放資源,並記錄日誌便於排查。
  2. 終結條件檢查:如對象必須處於「已歸還」狀態才能回收,finalize 中檢測並報警。
  3. 釋放非關鍵本地資源:如緩存句柄等。

finalize() 繼承鏈陷阱與守護者模式

問題:子類覆寫未調用 super.finalize() 或拋異常導致父類終結方法失效

class Son extends Parent {
    @Override protected void finalize() throws Throwable {
        System.out.println("Son finalize");
        int i = 5 / 0; // 異常中斷
        super.finalize(); // 永不執行
    }
}

運行結果:僅打印 Son finalize,父類邏輯缺失。

解決:終結方法守護者

class Parent2 {
    private final Object finalizeGuardian = new Object() {
        @Override protected void finalize() throws Throwable {
            System.out.println("父類終結邏輯必執行");
        }
    };
}

即使子類 finalize 異常中斷,守護者對象獨立 GC 仍會執行父類邏輯。

代碼驗證

public class FinalizeGuardian {
    public static void main(String[] args) {
        new Son2(); System.gc();
    }
}
// 輸出:
// 父類終結邏輯必執行
// Son2 finalize start
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved