JVM 1.2 以前,堆中對象僅分三種狀態:可觸及、可復活、不可觸及。1.2 版本引入四種強弱引用層級,精細控制對象生命週期:
SoftReference 引用,記憶體不足時才回收,適合快取。WeakReference 引用,GC 掃描到即回收,適合規範映射(如 WeakHashMap)。PhantomReference 引用,對象即將被回收,get() 永遠返回 null,用於比 finalize 更靈活的清理。ReferenceQueue 讓應用程式異步感知可觸及性變化。建立引用時關聯隊列:
ReferenceQueue<Cow> queue = new ReferenceQueue<>();
SoftReference<Cow> ref = new SoftReference<>(new Cow(1), queue);
GC 改變引用狀態時會將引用物件入隊。提供兩種取值方式:
poll():非阻塞,無元素立即返回 null。remove():阻塞等待,直到有元素。| 引用類型 | 回收時機 | 清除引用 | 入隊時機 | 典型用途 |
|---|---|---|---|---|
| SoftReference | 記憶體不足時 | 是 | 清除後 | 記憶體敏感快取 |
| WeakReference | 下次 GC 時 | 是 | 清除後 | 規範映射、監聽器快取 |
| PhantomReference | 即將回收時 | 否(需手動 clear) | 立即 | 資源釋放、最終化替代 |
public class TestReference {
public static void main(String[] args) throws InterruptedException {
ReferenceQueue<Cow> queue = new ReferenceQueue<>();
SoftReference<Cow> ref1 = new SoftReference<>(new Cow(1), queue);
final SoftReference<Cow> ref2 = new SoftReference<>(new Cow(2), queue);
System.out.println(queue.poll()); // null
ref1.enqueue(); // 手動入隊演示
System.out.println(queue.poll().get()); // Cow 1
new Thread(() -> {
try {
System.out.println(queue.remove().get()); // 阻塞等待 Cow 2
System.out.println("線程獲取到了引用隊列中的對象");
} catch (InterruptedException e) { e.printStackTrace(); }
}).start();
Thread.sleep(3000);
ref2.enqueue();
}
}
class Cow { private int num; public Cow(int n){num=n;} public String toString(){return "This is Cow "+num;} }
輸出:
null This is Cow 1 等待 3 秒鐘後把 cow2 編入隊列... This is Cow 2 線程獲取到了引用隊列中的對象
SoftReference,配合 ReferenceQueue 清理過期項目。WeakHashMap 或自行管理 WeakReference。finalize(),改用 PhantomReference + ReferenceQueue 實現確定性資源釋放。