Java 的垃圾回收機制(GC)是其核心優勢之一,而引用類型則是開發者與 GC 互動的關鍵介面。理解四種引用類型——強、軟、弱、虛擬引用,不僅能寫出更健壯的程式,更能在記憶體敏感場景(如快取、對象池)中遊刃有餘。
JVM 透過可達性分析判斷對象是否存活:從 GC Roots 出發,沒有任何引用鏈連接的對象即為不可達,成為回收候選者。引用類型決定了對象在不同記憶體壓力下的生存策略。
Object obj = new Object(); // 強引用
String str = "Hello"; // 強引用
OutOfMemoryError 也不回收強引用對象null 而終結public class StrongReferenceTest {
public static void main(String[] args) {
MyDate date = new MyDate(); // 強引用
System.gc(); // 手動觸發 GC
// 輸出:無任何輸出,date 未被回收
}
}
SoftReference ref = new SoftReference<>(new Obj());
public class SoftReferenceTest {
public static void main(String[] args) {
SoftReference ref = new SoftReference<>(new MyDate());
ReferenceTest.drainMemory(); // 模擬記憶體耗盡
// 輸出:無輸出(軟引用在記憶體不足時被回收)
}
}
// 等價邏輯:
// MyDate date = new MyDate();
// if (JVM.記憶體不足()) { date = null; System.gc(); }
WeakReference ref = new WeakReference<>(new Obj());
public class WeakReferenceTest {
public static void main(String[] args) {
WeakReference ref = new WeakReference<>(new MyDate());
System.gc(); // 觸發 GC
// 輸出:obj [Date: ...] is gc(弱引用被回收)
}
}
// 等價邏輯:
// MyDate date = new MyDate();
// if (JVM.記憶體不足()) { date = null; System.gc(); } // 弱引用無視記憶體是否充足
ReferenceQueue queue = new ReferenceQueue<>();
PhantomReference ref = new PhantomReference<>(new Obj(), queue);
get() 永遠返回 null),必須配合 ReferenceQueue 使用finalize() 執行完畢後,虛擬引用入隊finalize()
public class PhantomBuffer {
private byte[] data = new byte[0];
private ReferenceQueue queue = new ReferenceQueue<>();
private PhantomReference ref = new PhantomReference<>(data, queue);
public byte[] get(int size) {
if (size <= 0) throw new IllegalArgumentException("Wrong buffer size");
if (data.length < size) {
data = null; // 斷開強引用
System.gc(); // 強制 GC
try {
queue.remove(); // 阻塞等待舊緩衝區被回收
ref.clear(); // 手動清除虛擬引用
ref = null;
data = new byte[size]; // 分配新緩衝區
ref = new PhantomReference<>(data, queue);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return data;
}
}
此模式確保舊緩衝區完全回收後才分配新記憶體,將記憶體佔用控制在極低水平,適合行動裝置或記憶體受限環境。
public class PhantomReferenceTest {
public static void main(String[] args) {
ReferenceQueue queue = new ReferenceQueue<>();
PhantomReference ref = new PhantomReference<>(new MyDate(), queue);
System.gc();
// 輸出:obj [Date: ...] is gc(虛擬引用在 finalize 後入隊)
}
}
// 等價邏輯:
// MyDate date = new MyDate();
// date = null; // 虛擬引用不阻止回收,僅感知回收時機
| 引用類型 | 獲取對象 | GC 回收時機 | 主要用途 | ReferenceQueue 支援 |
|---|---|---|---|---|
| 強引用 | 直接獲取 | 從不回收 | 一般對象 | 不支援 |
| 軟引用 | ref.get() | 記憶體不足前 | 快取 | 支援 |
| 弱引用 | ref.get() | GC 掃描到即回收 | WeakHashMap、監聽器 | 支援 |
| 虛擬引用 | 永遠 null | finalize() 後 | 直接記憶體管理、對象池 | 必須配合 |
Guava Cache、Caffeine 內部即基於軟/弱引用實現Cleaner 或虛擬引用remove() 否則線程池場景會洩漏queue.remove() 或 queue.poll(),避免隊列積壓掌握四大引用類型,就是掌握了與 JVM 記憶體管理協作的核心技能。在快取設計、大對象管理、資源精準釋放等進階場景中,靈活運用軟、弱、虛擬引用,能讓你的 Java 應用在記憶體效能與穩定性上更上層樓。