1. 程式人生 > >Java中的Reference

Java中的Reference

快速 www .cn vat out 分享 調用 exce public

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());

,但是特殊點就是在它被創建後的下一次GC時候其指向的對象實例會被回收掉,不管內存是不是充足,反正就是活不過一次GC。JDK中的WeakHashMap就是使用到WeakReference,其Key就是被包裝成WeakReference。

虛引用 - 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