Java 提供 強引用、軟引用、弱引用、虛引用 四個層級,定義在 java.lang.ref 包。本文從 源碼定義、GC 回收行為、ReferenceQueue 聯動、實戰代碼驗證、典型應用場景 (緩存、對象池、內存敏感資源清理) 五個維度深度解析,附帶 JVM 參數驗證截圖級日誌分析。
| 引用類型 | 類名 | GC 回收時機 | get() 返回 | ReferenceQueue 入隊時機 | 典型用途 | 強度 |
|---|---|---|---|---|---|---|
| 強引用 | 無 (語言內置) | 永不回收 (寧願 OOM) | 直接返回對象 | 不支持 | 普通對象持有 | 最強 |
| 軟引用 | SoftReference |
內存不足前 (OOM 前最後嘗試) | 對象或 null | 回收後 | 內存敏感緩存 (圖片、網絡數據) | 強 |
| 弱引用 | WeakReference |
下次 GC 即回收 (發現即回收) | 對象或 null | 回收後 | WeakHashMap、類加載器、監聽器 | 中 |
| 虛引用 | PhantomReference |
隨時可能回收 (需配合 Queue) | 永遠 null | 回收前 (對象已死、未釋放內存) | DirectBuffer 釋放、原生資源清理、GC 監控 | 最弱 |
public abstract class Reference {
private T referent; // 被引用對象
private volatile ReferenceQueue super T> queue; // 關聯隊列
private Reference next; // 鏈表節點 (GC 內部使用)
private transient Object lock = new Object(); // 鎖
public T get() { return referent; }
public void clear() { this.referent = null; }
public boolean enqueue() { ... } // 入隊邏輯
}
class FinalReference extends Reference {
public FinalReference(T referent, ReferenceQueue super T> q) {
super(referent, q);
}
}
// 僅由 JVM 內部使用於 finalize() 機制,開發者不可直接使用
public class SoftReference extends Reference {
static private long clock; // 時間戳,用於優化 GC 決策
public SoftReference(T referent) { super(referent); }
public SoftReference(T referent, ReferenceQueue super T> q) { super(referent, q); }
}
public class WeakReference extends Reference {
public WeakReference(T referent) { super(referent); }
public WeakReference(T referent, ReferenceQueue super T> q) { super(referent, q); }
}
public class PhantomReference extends Reference {
public PhantomReference(T referent, ReferenceQueue super T> q) {
super(referent, q); // 必須提供 Queue,否則無意義
}
public T get() { return null; } // 覆寫強制返回 null
}
public class SoftRefTest {
static ReferenceQueue
日誌關鍵點:-XX:+PrintGCDetails 可見 Full GC 後軟引用被清理。
public class WeakRefTest {
static ReferenceQueue
public class PhantomRefTest {
static ReferenceQueue q = new ReferenceQueue<>();
public static void main(String[] args) throws InterruptedException {
Object obj = new Object();
PhantomReference ref = new PhantomReference<>(obj, q);
obj = null;
new Thread(() -> {
try { System.out.println("隊列收到: " + q.remove()); } catch (InterruptedException e) {}
}).start();
for (int i = 0; i < 5; i++) {
System.gc();
Thread.sleep(500);
}
}
}
輸出規律:
1. 第一次 GC:對象 finalize()、標記為可回收,**未入隊**。
2. 第二次 GC:內存真正釋放,**PhantomReference 入隊**。
3. 隊列線程收到引用,可執行清理邏輯。
public class BitmapCache {
private final Map> cache = new HashMap<>();
public void put(String url, Bitmap bitmap) {
cache.put(url, new SoftReference<>(bitmap));
}
public Bitmap get(String url) {
SoftReference ref = cache.get(url);
return ref != null ? ref.get() : null; // 可能返回 null (已被 GC)
}
}
// 內存緊張時 GC 自動清理部分圖片,不會 OOM;內存充足時盡量保留,提升命中率
// WeakHashMap 內部 Entry 繼承 WeakReference
// 當 Key 僅被 Map 持有 (無外部強引用) 時,GC 會回收 Key,Entry 自動從 Map 移除
Map cache = new WeakHashMap<>();
// 類加載器卸載時,緩存自動清理,防止內存洩漏
// Java NIO DirectBuffer 分配堆外內存
// Cleaner (繼承 PhantomReference) 註冊到 ReferenceQueue
// GC 發現 PhantomReference 入隊 → Cleaner.clean() → freeMemory(address)
// 避免堆外內存洩漏 (僅靠 finalize 不可靠,且延遲不可控)
public class NativeResource implements AutoCloseable {
private final long nativeHandle;
private final PhantomReference phantom;
public NativeResource() {
this.nativeHandle = allocateNative();
ReferenceQueue q = new ReferenceQueue<>();
this.phantom = new PhantomReference<>(this, q);
// 守護線程處理隊列
new Thread(() -> {
while (true) {
try {
PhantomReference ref = (PhantomReference) q.remove();
// 執行原生釋放
freeNative(ref.nativeHandle); // 需額外字段存儲 handle
} catch (InterruptedException ignored) {}
}
}).start();
}
@Override public void close() { freeNative(nativeHandle); phantom.clear(); }
}
AutoCloseable + try-with-resources,輔以 Cleaner (JDK 9+ 官方 PhantomReference 封裝)。四大引用類型構成了 Java 內存管理的「強度光譜」:**強引用為骨架、軟引用為緩存緩衝、弱引用為自動清理映射、虛引用為資源回收兜底**。深入理解它們與 GC、ReferenceQueue 的交互機制,不僅能寫出抗 OOM 的緩存框架、無洩漏的對象池、安全的堆外內存管理器,更能在面試中從容應對「強軟弱虛」經典題。建議動手跑通驗證代碼,配合 -XX:+PrintGCDetails -XX:+PrintReferenceGC 觀察 GC 日誌,將理論內化為直觀認知。