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

Java 記憶體洩漏解決方案(三):SoftHashMap 源碼深度解析與 Reference 機制實戰 Java 記憶體洩漏解決方案(三):SoftHashMap 源碼深度解析與 Reference 機制實戰

     編輯:Java 相關知識

系列最終篇,深度剖析 SoftHashMap 完整源碼,展示如何結合 SoftReference、ReferenceQueue、硬引用緩存雙層結構 實現「記憶體敏感的高效緩存」,徹底解決緩存導致的 OOM 風險。

設計目標

  • 緩存盡可能多熱點資料提升命中率。
  • JVM 記憶體不足時自動釋放軟引用物件,避免 OOM。
  • 近期訪問物件保留在硬引用鏈表中(LRU 風格),延緩被回收。

完整源碼解析


public class SoftHashMap extends AbstractMap {
    // 內部 HashMap 存儲 SoftReference
    private final Map hash = new HashMap();
    // 硬引用緩存大小上限(近期訪問物件)
    private final int HARD_SIZE;
    // 硬引用 FIFO 隊列(最近訪問在頭部)
    private final LinkedList hardCache = new LinkedList();
    // ReferenceQueue:GC 回收軟引用後通知
    private ReferenceQueue queue = new ReferenceQueue();

    public SoftHashMap() { this(100); }
    public SoftHashMap(int hardSize) { HARD_SIZE = hardSize; }

    // 核心:獲取值
    public Object get(Object key) {
        Object result = null;
        SoftReference softRef = (SoftReference) hash.get(key);
        if (softRef != null) {
            result = softRef.get(); // 可能為 null(已被 GC 回收)
            if (result == null) {
                hash.remove(key); // 清理無效條目
            } else {
                // 訪問命中:加入硬引用隊列頭部(LRU)
                hardCache.addFirst(result);
                if (hardCache.size() > HARD_SIZE) {
                    hardCache.removeLast(); // 淘汰最久未訪問
                }
            }
        }
        return result;
    }

    // 自定義 SoftReference:攜帶 key 以便從 queue 清理 hash
    private static class SoftValue extends SoftReference {
        private final Object key;
        SoftValue(Object value, Object key, ReferenceQueue q) {
            super(value, q);
            this.key = key;
        }
    }

    // 處理 ReferenceQueue:清理已被 GC 回收的條目
    public void processQueue() {
        SoftValue sv;
        while ((sv = (SoftValue) queue.poll()) != null) {
            hash.remove(sv.key); // 關鍵:用攜帶的 key 從 hash 移除
        }
    }

    // 放入新值
    public Object put(Object key, Object value) {
        processQueue(); // 先清理失效條目
        return hash.put(key, new SoftValue(value, key, queue));
    }

    public Object remove(Object key) {
        processQueue();
        return hash.remove(key);
    }

    public void clear() {
        hardCache.clear();
        processQueue();
        hash.clear();
    }

    public int size() {
        processQueue();
        return hash.size();
    }

    // 不支援 entrySet(避免暴露內部結構)
    public Set entrySet() {
        throw new UnsupportedOperationException();
    }
}

核心機制圖解

SoftHashMap 結構:
┌─────────────────────────────────────┐
│ hash: HashMap       │
│   Key → SoftValue(value, key, queue)│
└─────────────────────────────────────┘
                    │
                    ▼
┌─────────────────────────────────────┐
│ hardCache: LinkedList        │ (硬引用,防止近期熱點被 GC)
│ Head(最近訪問) ... Tail(最久未訪問) │
│ 大小限制 HARD_SIZE (預設 100)       │
└─────────────────────────────────────┘
                    │
                    ▼
┌─────────────────────────────────────┐
│ queue: ReferenceQueue               │
│ GC 回收 SoftValue 後 enqueue        │
│ processQueue() 輪詢清理 hash        │
└─────────────────────────────────────┘

關鍵設計細節

機制作用關鍵代碼
SoftReference記憶體不足時 GC 可回收值new SoftValue(value, key, queue)
ReferenceQueue異步通知哪些軟引用被回收queue.poll() 在 get/put/size/clear 時調用
硬引用緩存近期訪問物件強引用持有,不被 GChardCache.addFirst(result)
LRU 淘汰硬引用超限移除尾部hardCache.removeLast()
攜帶 keyqueue 中無 value 時仍能定位 hash 條目SoftValue.key
懶性清理各公共方法調用 processQueue()get/put/remove/size/clear 首行

使用範例


// 緩存圖片、模板、查詢結果等「生成成本高、記憶體佔用大、可重算」資料
SoftHashMap cache = new SoftHashMap(200); // 保留 200 個硬引用

// 存入
cache.put("user:1001", loadUserFromDB(1001));

// 取出
User u = (User) cache.get("user:1001");
if (u == null) {
    u = loadUserFromDB(1001);
    cache.put("user:1001", u);
}

適用場景 vs 不適用場景

系列總結

  1. 第一篇:記憶體管理機制、洩漏成因(游離物件)、一般避免原則。
  2. 第二篇:四種引用類型(強/軟/弱/虛)、GC 交互細節、ReferenceQueue 配合模式。
  3. 第三篇(本文):SoftHashMap 實戰源碼,軟引用+硬引用雙層緩存實現。

掌握 Reference 機制,你就擁有了在「效能與記憶體安全」間遊刃有餘的架構能力。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
適用不適用
圖片/模板/大物件緩存極高頻、極低延遲要求(用 Caffeine/Guava Cache)
生命週期長、重建成本高需強一致性、事務性資料
記憶體敏感環境(Android、嵌入式)資料量固定小、適合強引用 Map