Java中的Reference
Java中的Reference
1. 常用四種引用
快速記憶法:“硬(俗稱的強引用) --> 軟(SoftReference) --> 弱(WeakReference) --> 虛(PhantomReference)”
此處將常說的“強引用”記憶成“硬引用”可以對應到次席的“軟引用”(反義詞:硬-軟)這樣更容易記住
強引用
平常我們代碼中寫到的引用類型都是強引用類型,比如Object obj = new Object();
, Object實例就有一個強引用類型指向它,在GC過程中即使發生OOM,該Object實例都不會被回收。
軟引用 - SoftReference
定義方式:SoftReference sr = new SoftReference(new Object());
一個對象的實例被一個軟引用實例指向,那麽在GC過程中發生OOM之前,該Object對象實例會被回收掉,在內存充足的情況下是不會被回收的。同時可以將一個引用隊列關聯到該軟引用上,在軟引用指向的對象被回收後,該軟引用會被加入到關聯的引用隊列中。我們可以通過Reference的get()方法獲取到該軟引用指向的對象實例。
弱引用 - WeakReference
弱引用基本上同上面的軟引用類似,WeakReference wr = new WeakReference(new Object());
虛引用 - PhantomReference
定義方式:PhantomReference pr = new PhantomReference(new Object(), new ReferenceQueue())
,虛引用對象再被定義時,必須指定一個引用隊列實例。JDK文檔中介紹它主要用於對象被回收前資源的釋放操作,替換finalize()
方法。它和前面的兩個軟引用和弱引用不同的地方有兩點:
2. 驗證
虛引用在對象被回收之前添加到引用隊列中,同時需要手動處理,它指向的對象才會被回收
思路:
創建一個虛引用對象,然後發起一次GC操作,查看其指向的對象實例是否被回收
通過檢測它關聯的引用隊列,取出加入的虛引用對象,查看此時其指向的對象實例是否被回收
調用虛引用對象的clear方法之後,查看其指向的對象實例是否被回收
2.1. 代碼
public class ReferenceApp1 {
public static void main(String[] args) throws Exception {
phantom();
}
/**
* 驗證PhantomReference
* @throws Exception
*/
private static void phantom() throws Exception {
// 步驟1. 定義一個InnerPhantomRefObj對象實例
InnerPhantomRefObj innerPhantomRefObj = new InnerPhantomRefObj();
innerPhantomRefObj.setName("InnerPhantomRefObj-1");
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
// 步驟2. 定義一個虛引用對象
PhantomReference<Object> phtRef = new PhantomReference<>(innerPhantomRefObj, referenceQueue);
// 移除InnerPhantomRefObj對象實例上的強引用,不然後面操作不會被回收
innerPhantomRefObj = null;
System.err.println("before gc | get PhantomReference referent:" + phtRef.get());
// 步驟3. 發起GC操作
System.gc();
int i = 0;
Reference tmp = null;
while(true) {
System.out.println("phantom iteration >>>> " + ++i);
Thread.sleep(5000);
if(i == 1) {
// 步驟4. 從引用隊列中取出虛引用對象
tmp = referenceQueue.poll();
if(tmp != null) {
System.err.println("get PhantomReference from ReferenceQueue");
}
// 發起一次GC操作,其實此時InnerPhantomRefObj對象不會被回收
System.gc();
}
if(i == 5) {
if(tmp != null) {
System.err.println("after gc | get PhantomReference referent:" + tmp.get());
// 步驟5. 調用虛引用上的clear方法,讓下一次GC操作回收掉InnerPhantomRefObj對象
tmp.clear();
// 或者讓GC操作釋放PhantomReference對象實例
// tmp = null;
// phtRef = null;
System.err.println("clear PhantomReference");
// 發起一次GC操作
System.gc();
}
}
if(i == 10) {
break;
}
}
}
@Data
private static class InnerPhantomRefObj {
private String name;
}
}
2.2. 觀察VisualVM中實例個數變化判斷是否被回收
從上述代碼的步驟1到步驟5之間的實例統計截圖如:(實例個數為1,沒有被回收)
執行步驟5(調用PhantomReference的clear方法)之後的實例統計截圖如:(實例個數為0,已被被回收)
3. 類比
看到一篇英文博客中用一個例子來類比軟、弱、虛引用三者之間的差別非常好,在此借用一下:
比如一個快餐店中,桌子座位有限,服務員會隨時清理桌子座位,你進去點單,找到一個座位坐下,會存在下面幾種情況
然後後面有很多人過來點單時,當座位不夠時你會讓出座位,但在此之前每次服務員過來清理座位時你都沒有讓出座位,這種情況就像就像軟引用
第一次服務員過來清理桌子座位時,你就讓出座位,這種情況就像弱引用
第一次服務員過來清理桌子座位時,你可以隨時準備讓出座位,其實這時候你並沒有讓出位置,但是後面服務員說出一句讓你讓出座位時你才會讓出座位,這種情況就像虛引用
【參考】weak-soft-and-phantom-references-in-java-and-why-they-matter
作者:sv
出處:https://www.cnblogs.com/sv00
版權聲明:本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。
Java中的Reference