Java 定義四種引用強度(由強到弱),決定對象被 GC 回收的時機與條件:
| 引用類型 | 類名 | 回收時機 | 典型用途 |
|---|---|---|---|
| 強引用 | 普通常量 | 永不主動回收(OOM 前) | 正常對象持有 |
| 軟引用 | SoftReference | 內存不足前 | 緩存、圖片池 |
| 弱引用 | WeakReference | 下次 GC 時 | WeakHashMap、監聽器 |
| 虛引用 | PhantomReference | 最終化前,需配合 Queue | 直接內存釋放、資源清理追蹤 |
| 終結引用 | FinalReference | finalize() 執行後 | 內部實現 finalize 機制 |
public abstract class Reference {
private T referent; // 被引用對象
private volatile ReferenceQueue super T> queue; // 關聯隊列
@SuppressWarnings("unchecked")
Reference discovered; // GC 發現鏈表指針
// ... 狀態由 (queue, next, discovered) 隱式編碼
}
queue != null && queue != ENQUEUED && discovered == null,對象強可達。discovered != null,GC 發現引用對象可回收,掛載在 Reference.pending 靜態鏈表等待處理。queue == ENQUEUED,已入隊 ReferenceQueue,等待用戶線程輪詢。queue == null,用戶線程調用 clear() 後,referent 已置 null。非標準隊列結構,而是無鎖單向鏈表,head 指向最新入隊節點(LIFO)。
// 入隊
r.queue = ENQUEUED;
r.next = (head == null) ? r : head;
head = r;
queueLength++;
lock.notifyAll(); // 喚醒阻塞在 remove() 的線程
// 出隊
Reference extends T> r = head;
if (r != null) {
head = r.next;
r.next = null;
r.queue = null; // 狀態轉 Inactive
}
return r;
SoftRefPolicy)決定是否清除。referent,掛載 discovered 鏈。get() 獲取對象進行清理(如 DirectBuffer 釋放)。finalize(),完成後清除 referent 並入隊。MAX_PRIORITY,死循環調用 tryHandlePending():
pending 鏈取出節點。ReferenceQueue,調用 queue.enqueue(r) 入隊。r.clear() 置空 referent。queue.remove() 或 poll() 獲取 Reference,執行清理邏輯(關閉流、釋放堆外內存、移除緩存條目等)。原生 ReferenceQueue 需自行啟動守護線程輪詢,易漏寫。Guava 封裝:
// 定義帶清理邏輯的引用
class MyFinalizableRef extends FinalizableReference {
MyFinalizableRef(MyResource referent, FinalizableReferenceQueue queue) {
super(referent, queue);
}
@Override
public void finalizeReferent() {
referent.close(); // 釋放資源
}
}
// 使用
FinalizableReferenceQueue queue = new FinalizableReferenceQueue();
MyResource res = new MyResource();
new MyFinalizableRef(res, queue); // 資源不可達時自動調用 close()
PhantomReference.get() 永遠返回 null(JDK 9+ 修正為返回 referent,但需 ref.refersTo() 判斷)。LinkedHashMap 或 Caffeine 等專用緩存庫。WeakHashMap 鍵為弱引用,值為強引用,值對象不可強引用鍵,否則鍵永不回收。finalizeReferent() 或隊列消費中,不要重寫 finalize()(已棄用,性能差、不可靠)。DirectByteBuffer) 必須用 PhantomReference + Cleaner (JDK 9+) 釋放,避免 OOM。// 替代 PhantomReference + ReferenceQueue
Cleaner cleaner = Cleaner.create();
Cleaner.Cleanable cleanable = cleaner.register(obj, () -> {
// 清理邏輯,在 obj 變為 phantom reachable 時執行
freeNativeMemory(address);
});
// 可手動提前清理
cleanable.clean();
Cleaner 內部使用 PhantomReference,但提供更簡潔 API、線程池隔離、可取消註冊,推薦替代手動 ReferenceQueue 管理。