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

Java 理論與實踐:善用軟參考防止記憶體洩漏

     編輯:Java 相關知識

物件游離:隱形的記憶體殺手

垃圾收集器(GC)雖自動管理記憶體,但若破壞物件生命週期管理,仍會發生記憶體洩漏。典型案例——LeakyChecksum 為了「優化」將緩衝區提升為實例欄位:

public class LeakyChecksum {
    private byte[] byteArray; // 實例欄位緩存緩衝區
    public synchronized int getFileChecksum(String fileName) {
        int len = getFileSize(fileName);
        if (byteArray == null || byteArray.length < len)
            byteArray = new byte[len]; // 只增不減
        readFileContents(fileName, byteArray);
        // 計算 checksum...
    }
}

後果:緩衝區永不釋放,隨最大檔案增長永久佔用記憶體,增加 GC 壓力。根本原因:邏輯上屬於方法區域的變數,被人為延長生命週期至實例層級。

軟參考:讓 GC 協助快取管理

弱參考不阻礙 GC 回收;軟參考則允許應用程式指定物件為「可犧牲」:記憶體充足時保留,記憶體緊張時由 GC 回收。GC 在拋出 OutOfMemoryError 前必須清除所有軟參考。

修正版:使用 SoftReference 管理緩衝區

public class CachingChecksum {
    private SoftReference<byte[]> bufferRef;
    public synchronized int getFileChecksum(String fileName) {
        int len = getFileSize(fileName);
        byte[] byteArray = bufferRef.get(); // 可能返回 null
        if (byteArray == null || byteArray.length < len) {
            byteArray = new byte[len];
            bufferRef.set(byteArray); // 更新軟參考
        }
        readFileContents(fileName, byteArray);
        // 計算 checksum...
    }
}

現在緩衝區在記憶體允許時保留,壓力大時自動回收,無需手動管理生命週期。

廉價快取策略與陷阱

  • 單一物件快取:直接使用 SoftReference<T>。
  • 多物件快取:推薦 SoftReference<Map<K,V>>(整個 Map 可一次回收),而非 Map<K, SoftReference<V>>(GC 工作量大)。
  • 避免用弱參考做快取:弱參考在下次 Minor GC 即被清除,快取命中率極低。
  • 適用場景:GUI 位圖快取、可重算的計算結果。高效能要求系統仍需專業快取框架(支援淘汰策略、事務、複寫)。

GC 如何處理參考物件

參考物件(Reference 子類)在 GC 追蹤階段不被標記,而是放入已知活躍參考佇列。追蹤結束後:

  1. 識別軟可達物件,依回收記憶體量與策略決定是否清除;清除者進入參考佇列(若有),其餘視為新根集繼續追蹤。
  2. 處理弱可達物件:全部清除並入佇列。
所有參考類型入佇列前均被清除,因此佇列處理執行緒無法存取被參考物件,只能存取參考物件本身。

參考物件的效能成本

每次 GC 必須建構活躍參考物件列表並逐一處理,帶來固定開銷。參考物件本身也受 GC 管理,可能在被參考物件前被回收(此時不入佇列)。

陣列基礎集合的物件游離

陣列實作堆疊/環狀緩衝區時,pop() 後陣列仍持有參考阻礙 GC:

public class LeakyStack {
    private Object[] elements = new Object[MAX_ELEMENTS];
    private int size = 0;
    public Object pop() {
        if (size == 0) throw new EmptyStackException();
        Object result = elements[--size];
        // elements[size] = null; // 必須手動清除!
        return result;
    }
}

修正:彈出後將陣列位置設為 null。鏈結實作無此問題(節點生命週期自動綁定)。若堅持陣列優化,開發者必須嚴謹管理參考生命週期。

結語

軟參考與弱參考同樣利用 GC 協助防止物件游離。僅當應用程式可容忍大量快取物件被回收時,軟參考才為適宜選擇。

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