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

Java 四大引用類型深度解析:強、軟、弱、虛引用

     編輯:Java 相關知識

引用類型層級關係

強引用 > 軟引用 > 弱引用 > 虛引用
(生命週期最長)                    (生命週期最短)
引用類型建立方式GC 回收時機典型用途獲取對象
強引用Object obj = new Object()**永不回收** (寧願 OOM)普通物件持有直接 obj
軟引用SoftReference<T> ref = new SoftReference<>(obj)**記憶體不足時** 回收**記憶體敏感快取** (圖片、網路資料)ref.get() 可能為 null
弱引用WeakReference<T> ref = new WeakReference<>(obj)**下次 GC 時** 回收**非強制性映射** (ThreadLocal、ClassLoader、監聽器)ref.get() 可能為 null
虛引用PhantomReference<T> ref = new PhantomReference<>(obj, queue)**隨時可能** 回收**精確追蹤回收**、**堆外記憶體釋放** (DirectByteBuffer)ref.get() 永遠返回 null

代碼演示

public class ReferenceDemo {
    public static void main(String[] args) throws InterruptedException {
        // 1. 強引用
        Object strong = new Object();
        System.out.println("強引用: " + strong); // 永不為 null

        // 2. 軟引用
        SoftReference<Object> soft = new SoftReference<>(new Object());
        System.gc(); // 記憶體充足不回收
        System.out.println("GC後軟引用: " + soft.get()); // 仍存在
        
        // 模擬 OOM (需 -Xmx10m)
        // List<byte[]> list = new ArrayList<>();
        // while (true) list.add(new byte[1024*1024]); // 軟引用會被清理

        // 3. 弱引用
        WeakReference<Object> weak = new WeakReference<>(new Object());
        System.gc(); // 下次 GC 回收
        System.out.println("GC後弱引用: " + weak.get()); // 可能為 null

        // 4. 虛引用 + ReferenceQueue
        ReferenceQueue<Object> queue = new ReferenceQueue<>();
        PhantomReference<Object> phantom = new PhantomReference<>(new Object(), queue);
        System.out.println("虛引用 get(): " + phantom.get()); // 永遠 null
        
        // 當對象被回收,phantom 會被放入 queue
        new Thread(() -> {
            try {
                Reference<?> ref = queue.remove(); // 阻塞等待
                System.out.println("對象已被回收,執行清理邏輯: " + ref);
                // 釋放堆外記憶體、關閉資源等
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }).start();
        
        System.gc();
        Thread.sleep(1000);
    }
}

實戰應用場景

1. 軟引用:圖片快取 (LruCache 內部原理)

public class ImageCache {
    private final Map<String, SoftReference<Bitmap>> cache = new HashMap<>();
    
    public void put(String key, Bitmap bitmap) {
        cache.put(key, new SoftReference<>(bitmap));
    }
    
    public Bitmap get(String key) {
        SoftReference<Bitmap> ref = cache.get(key);
        return ref != null ? ref.get() : null; // 可能已被 GC 回收
    }
}
// 配合 LruCache 使用:記憶體緊張時自動淘汰軟引用持有的 Bitmap

2. 弱引用:ThreadLocal 實現原理

// ThreadLocalMap Entry 繼承 WeakReference>
static class Entry extends WeakReference> {
    Object value;
    Entry(ThreadLocal k, Object v) {
        super(k);
        value = v;
    }
}
// 當 ThreadLocal 無強引用時,Entry 自動變弱可達,GC 時清理 value,防止記憶體洩漏

3. 弱引用:監聽器模式避免洩漏

public class EventBus {
    private final List> listeners = new ArrayList<>();
    
    public void register(Listener l) {
        listeners.add(new WeakReference<>(l));
    }
    
    public void post(Event e) {
        listeners.removeIf(ref -> ref.get() == null); // 清理已回收
        for (WeakReference ref : listeners) {
            Listener l = ref.get();
            if (l != null) l.onEvent(e);
        }
    }
}
// 監聽者無需顯式 unregister,GC 自動清理

4. 虛引用:DirectByteBuffer 堆外記憶體釋放

// Java 內部 Cleaner 機制 (Java 9+)
public class DirectBufferCleaner {
    public static void main(String[] args) {
        ByteBuffer buf = ByteBuffer.allocateDirect(1024 * 1024); // 1MB 堆外
        // 內部建立 PhantomReference + ReferenceQueue
        // GC 時 Cleaner 執行 freeMemory(address)
    }
}
// 傳統 finalize() 已棄用,PhantomReference + Cleaner 是標準替代

ReferenceQueue 參考隊列

  • 軟/弱/虛引用建立時可選擇性傳入 ReferenceQueue
  • 當引用對象被 GC 判定為可回收,**引用對象本身**會被放入隊列
  • 專用執行緒輪詢 queue.remove() 或 poll() 執行清理邏輯
  • 虛引用**必須**配合隊列使用,否則無意義

總結

  1. 強引用:預設、最強、寧願 OOM 不回收
  2. 軟引用:快取首選、記憶體不足才回收、配合 LRU
  3. 弱引用:輔助映射、ThreadLocal、監聽器、GC 即回收
  4. 虛引用:回收追蹤、堆外記憶體釋放、必配 ReferenceQueue
  5. **獲取對象統一用 ref.get(),需檢查 null** (虛引用永遠 null)
  6. 理解四者 GC 行為差異,能設計出**記憶體友好、無洩漏**的 Java 系統
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved