當物件可達性發生變化,GC 會將對應的 Reference 物件加入註冊的 ReferenceQueue。這是 JVM 與應用程式協作的橋樑,比輪詢 ref.get() == null 更高效、更即時。
| 參考類型 | 入隊時機 | get() 入隊前 | get() 入隊後 | 典型用途 |
|---|---|---|---|---|
| SoftReference | 記憶體不足前 | 物件 | null | 快取 |
| WeakReference | GC 發現弱可達 | 物件 | null | Canonicalizing 對應、監聽器 |
| PhantomReference | finalize 完成、記憶體回收前 | null | null | 精準資源釋放、記憶體外資源管理 |
| FinalReference | finalize 完成 | 物件 | null | 內部實作 finalize |
get() 永遠回傳 null,防止「物件復活」finalize() 執行完畢、記憶體真正回收前 → 確保所有清理邏輯已完成ref.clear(),配合佇列消費者模式精確控制public final class ResourceManager {
// 鍵 -> 資源映射 (WeakHashMap: 鍵無強引用時自動移除)
private final Map
interface Resource {
void use(Object... args);
void release();
}
class ResourceImpl implements Resource {
private final int keyHash;
private volatile boolean released = false;
private final NativeHandle handle; // 假設的原生資源控制代碼
ResourceImpl(Object key) {
this.keyHash = System.identityHashCode(key);
this.handle = NativeLibrary.acquire(); // 獲取原生資源
}
@Override
public synchronized void use(Object... args) {
if (released) throw new IllegalStateException("Resource released");
if (System.identityHashCode(args[0]) != keyHash)
throw new IllegalArgumentException("Invalid key");
handle.operate(args);
}
@Override
public synchronized void release() {
if (!released) {
released = true;
handle.close(); // 釋放原生資源
}
}
}
System.identityHashCode(key) 防止鍵被偽造/替換release() 加 synchronized,支援多執行緒並發釋放shutdown() 优雅停止// 更簡潔、標準化的資源清理機制
public class ResourceManager {
private static final Cleaner cleaner = Cleaner.create();
public Resource acquire(Object key) {
ResourceImpl res = new ResourceImpl();
// 註冊清理動作:key 不可達時執行 res.release()
cleaner.register(key, res::release);
return res;
}
}
優點:無需手動管理執行緒/佇列,JVM 內建優化。缺點:清理動作在專用執行緒執行,順序不保證,不適合有嚴格順序依賴的資源。
ReferenceQueue + PhantomReference 是 Java 實現確定性資源釋放的核心機制,適用於原生記憶體、檔案控制代碼、連線池等非堆資源管理。Java 9+ 推薦使用 Cleaner 簡化開發。掌握參考類型語義差異、入隊時機、同步控制,是編寫高可靠系統級 Java 程式的關鍵技能。