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

Java/Android 四大引用類型全解析:強、軟、弱、虛引用

     編輯:Java 相關知識

Java 與 Android 運行時提供四種引用層級,決定物件在 GC 掃描時的存活優先權。理解其語義差異、回收時機與典型應用場景,對防止記憶體洩漏、實現快取、管理資源生命週期至關重要。

引用層級總覽

引用類型類別回收時機典型用途get() 可能為 null
強引用-永不主動回收(OOM 前)一般物件持有否
軟引用SoftReference記憶體不足時記憶體敏感快取(圖片、網路資料)是
弱引用WeakReference下次 GC 時監聽器、回調、ThreadLocalKey、ClassLoader 快取是
虛引用PhantomReference被回收後精確追蹤回收時機、直接記憶體釋放、清理動作永遠為 null

1. 強引用

Object obj = new Object(); // 強引用
// obj = null 且無其他強引用指向 → 可被 GC 回收

最常見形式。強引用存在期間,GC 絕不 回收該物件,寧願拋 OutOfMemoryError。

2. 軟引用 — 記憶體不足才回收

SoftReference<Bitmap> softRef = new SoftReference<>(bitmap);
// 記憶體充足:softRef.get() 返回物件
// 記憶體告急:GC 會清理軟引用指向的物件,get() 返回 null

特性:

  • 「寧可拋 OOM 也要保留」的語義已過時,現代 JVM/Android 會在拋 OOM 前積極清理軟引用。
  • 適合實現 記憶體敏感快取:如圖片載入庫、網路回應快取。
  • 可配合 ReferenceQueue 監聽真正被回收的瞬間。

3. 弱引用 — 下次 GC 即回收

WeakReference<Listener> weakListener = new WeakReference<>(listener);
// 無強引用時,下一次 GC(Young/Full)即會回收,get() 返回 null

核心場景:

  1. 防止記憶體洩漏的監聽器/回調:持有弱引用不阻礙目標物件回收。
  2. ThreadLocal 洩漏修復:ThreadLocalMap 的 Key 即為 WeakReference<ThreadLocal>。
  3. ClassLoader 快取:類卸載時自動清理。

Android 經典案例:Handler 洩漏修復

public class MainActivity extends AppCompatActivity {
    // 靜態內部類 + 弱引用持有外部 Activity
    private static class SafeHandler extends Handler {
        private final WeakReference<MainActivity> ref;
        SafeHandler(MainActivity act) { ref = new WeakReference<>(act); }
        
        @Override public void handleMessage(Message msg) {
            MainActivity act = ref.get();
            if (act == null) return; // Activity 已銷毀,放棄處理
            // 正常更新 UI
            act.updateUI(msg);
        }
    }
    private final SafeHandler handler = new SafeHandler(this);
}

非靜態內部類隱含持有外部類引用,導致 Activity 無法回收。靜態內部類 + WeakReference 打破循環引用。

4. 虛引用 — 回收後通知,get() 永遠為 null

ReferenceQueue<Object> queue = new ReferenceQueue<>();
PhantomReference<Object> phantom = new PhantomReference<>(obj, queue);
// phantom.get() 永遠返回 null
// 當 obj 被 GC 回收後,phantom 會被 enqueue 進 queue
// 專用線程輪詢 queue.poll() 執行清理邏輯(如 DirectBuffer 釋放)

唯一構造器:PhantomReference(T referent, ReferenceQueue<? super T> q) —— 必須 綁定 ReferenceQueue。

核心用途:

  • NIO DirectByteBuffer 釋放堆外記憶體(Cleaner 內部即用虛引用)。
  • 精確感知物件被回收的瞬間,執行資源釋放、日誌審計等終結動作。
  • 替代已棄用的 finalize(),避免其不可預測的執行時機與效能問題。

ReferenceQueue:三類引用的共同「回收信箱」

ReferenceQueue<MyObject> rq = new ReferenceQueue<>();
WeakReference<MyObject> wr = new WeakReference<>(obj, rq);

// 當 obj 真正被 GC 回收後(非僅進入最終化隊列),wr 會被放入 rq
// 專用線程處理:
new Thread(() -> {
    while (true) {
        Reference<?> ref = rq.remove(); // 阻塞等待
        // 執行清理:關閉檔案、釋放連線、移除快取條目等
    }
}).start();

關鍵區別:

  • get() == null:物件已進入最終化/回收流程,但尚未確定回收完成。
  • 出現在 ReferenceQueue:物件確已回收完成,可安全執行後續清理。

Android 開發特別提醒

  1. 避免在 Activity/Fragment 長生命週期物件中持有強引用(如單例、靜態變數、執行緒)。
  2. ImageLoader、Glide、Picasso 內部均使用弱/軟引用實現圖片快取,無需手動管理。
  3. LeakCanary 原理即基於弱引用 + ReferenceQueue 監測物件是否預期回收。
  4. Java 9+ 引入 java.lang.ref.Cleaner 作為 PhantomReference 的高層封裝,優先考慮使用。

總結選型指南

需求推薦引用理由
普通物件持有強引用預設行為,簡單直接
記憶體快取,希望盡量保留軟引用記憶體緊張才清理
監聽器/回調,不阻礙目標回收弱引用下次 GC 即回收
需精確感知回收瞬間執行清理虛引用 + ReferenceQueue回收後通知,get() 為 null
ThreadLocal Key弱引用JDK 內建機制
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved