強軟弱虛四種引用,看完不再犯迷糊

其實強引用、軟引用、弱引用、虛引用這四個概念非常簡單好記。

在開頭先總結一下這四個引用的特點吧。

  • 強引用:gc時不會回收
  • 軟引用:只有在內存不夠用時,gc纔會回收
  • 弱引用:只要gc就會回收
  • 虛引用:是否回收都找不到引用的對象,僅用於管理直接內存

接下來詳細看看這四種引用,結合代碼,深刻的體會一下。

強引用

即我們平時最常見的:

Object object = new Object();

image

只要一個對象有強引用,垃圾回收器就不會進行回收。即便內存不夠了,拋出OutOfMemoryError異常也不會回收。

上demo,注意註釋

/**
 * 一個對象
 * 重寫finalize方法,可以知道已經被回收的狀態
 *
 * @author 
 * @date 2020-05-23
 */
public class OneObject {
    @Override
    protected void finalize() throws Throwable {
        System.out.println("啊哦~OneObject被回收了");
    }
}

/**
 * 強引用例子
 *
 * @author 
 * @date 2020-05-23
 */
public class ShowStrongReference {
    public static void main(String[] args) {
        // 直接new一個對象,就是強引用
        OneObject oneObject = new OneObject();
        System.out.println("輸出對象地址:" + oneObject);
        System.gc();
        System.out.println("第一次gc後輸出對象地址:" + oneObject);
        oneObject = null;
        System.gc();
        System.out.println("置爲null後gc輸出對象地址:" + oneObject);
    }
}

執行代碼,可以看到以下輸出:

輸出對象地址:com.esparks.pandora.learning.references.OneObject@72ea2f77
第一次gc後輸出對象地址:com.esparks.pandora.learning.references.OneObject@72ea2f77
置爲null後gc輸出對象地址:null
啊哦~OneObject被回收了

軟引用

需要通過SoftReference對象實現:

SoftReference<OneObject> oneObjectSr = new SoftReference<>(new OneObject());

image

當內存足夠的時候,垃圾回收器不會進行回收。當內存不夠時,就會回收只存在軟引用的對象釋放內存。

常用於本地緩存處理。

上demo,注意註釋

/**
 * 軟引用
 * 內存不夠了就會回收
 * 注意,運行時需要保證heap大小爲35m,即小於實驗中全部對象的大小,才能觸發gc
 * -Xmx35m
 *
 * @author 
 * @date 2020-05-23
 */
public class ShowSoftReference {
    public static void main(String[] args) {
        // 我們需要通過SoftReference來創建軟引用
        SoftReference<OneObject> oneObjectSr = new SoftReference<>(new OneObject());
        // 我們這裏創建一個大小爲20m的數組
        SoftReference<byte[]> arraySr = new SoftReference<>(new byte[1024 * 1024 * 20]);
        System.out.println("軟引用對象oneObjectSr的地址:" + oneObjectSr);
        System.out.println("通過oneObjectSr關聯的oneObject對象的地址:" + oneObjectSr.get());
        System.out.println("數組的地址:" + arraySr.get());
        System.gc();
        System.out.println("正常gc一次之後,oneObject對象並沒有回收。地址" + oneObjectSr.get());

        // 再創建另一個大小爲20m的數組,這樣heap就不夠大了,從而系統自動gc。如果依舊不夠,會把已有的軟引用關聯的對象都回收掉。
        System.out.println("創建另一個大小爲20m的數組otherArray");
        byte[] otherArray = new byte[1024 * 1024 * 20];
        System.out.println("otherArray的地址:" + otherArray);

        // gc後,軟引用對象還在,但是通過軟引用對象創建的對象就被回收了
        System.out.println("現在srObject的地址:" + arraySr);
        System.out.println("現在srObject中oneObject對象的地址:" + arraySr.get());
        System.out.println("剛纔的數組對象也被回收啦,地址:" + arraySr.get());
    }
}

執行代碼,可以看到以下輸出:

軟引用對象oneObjectSr的地址:java.lang.ref.SoftReference@72ea2f77
通過oneObjectSr關聯的oneObject對象的地址:com.esparks.pandora.learning.references.OneObject@33c7353a
數組的地址:[B@681a9515
正常gc一次之後,oneObject對象並沒有回收。地址com.esparks.pandora.learning.references.OneObject@33c7353a
創建另一個大小爲20m的數組otherArray
啊哦~OneObject被回收了
otherArray的地址:[B@3af49f1c
現在srObject的地址:java.lang.ref.SoftReference@19469ea2
現在srObject中oneObject對象的地址:null
剛纔的數組對象也被回收啦,地址:null

弱引用

需要通過WeakReference對象實現:

WeakReference<OneObject> oneObjectWr = new WeakReference<>(new OneObject());

image

只要發生gc,就會回收只存在弱引用的對象。

常用於Threadlocal。

上demo,注意註釋

/**
 * 弱引用
 * 只要gc就會回收
 *
 * @author 
 * @date 2020-05-23
 */
public class ShowWeakReference {
    public static void main(String[] args) {
        // 我們需要通過WeakReference來創建弱引用
        WeakReference<OneObject> objectWr = new WeakReference<>(new OneObject());
        System.out.println("弱引用objectWr的地址:" + objectWr);
        System.out.println("弱引用objectWr關聯的oneObject對象的地址:" + objectWr.get());

        System.gc();

        // gc後,弱引用對象還在,但是通過弱引用對象創建的對象就被回收了
        System.out.println("gc後,弱引用objectWr的地址:" + objectWr);
        System.out.println("gc後,弱引用objectWr關聯的oneObject對象的地址:" + objectWr.get());
    }
}

執行代碼,可以看到以下輸出:

弱引用objectWr的地址:java.lang.ref.WeakReference@72ea2f77
弱引用objectWr關聯的oneObject對象的地址:com.esparks.pandora.learning.references.OneObject@33c7353a
gc後,弱引用objectWr的地址:java.lang.ref.WeakReference@72ea2f77
gc後,弱引用objectWr關聯的oneObject對象的地址:null
啊哦~OneObject被回收了

虛引用

需要通過PhantomReference對象和ReferenceQueue實現:

private ReferenceQueue<OneObject> queue = new ReferenceQueue<>();
PhantomReference<OneObject> oneObjectPr = new PhantomReference<>(new OneObject(), queue);

image

無論是否gc,其實都獲取不到通過PhantomReference創建的對象。

其僅用於管理直接內存,起到通知的作用。

這裏補充一下背景。因爲垃圾回收器只能管理JVM內部的內存,無法直接管理系統內存的。對於一些存放在系統內存中的數據,JVM會創建一個引用指向這部分內存。
image

當這個引用在回收的時候,就需要通過虛引用來管理指向的系統內存。這裏還需要依賴一個隊列來實現。當觸發gc對一個虛引用對象回收時,會將虛引用放入創建時指定的ReferenceQueue中。之後單獨對這個隊列進行輪訓,並做額外處理。
image

上demo,注意註釋

/**
 * 虛引用
 * 只用於管理直接內存,起到通知的作用
 *
 * @author 
 * @date 2020-05-23
 */
public class ShowPhantomReference {
    /**
     * 虛引用需要的隊列
     */
    private static final ReferenceQueue<OneObject> QUEUE = new ReferenceQueue<>();

    public static void main(String[] args) {
        // 我們需要通過WeakReference來創建虛引用
        PhantomReference<OneObject> objectPr = new PhantomReference<>(new OneObject(), QUEUE);
        System.out.println("虛引用objectPr的地址:" + objectPr);
        System.out.println("虛引用objectPr關聯的oneObject對象的地址:" + objectPr.get());

        // 觸發gc,然後檢查隊列中是否有虛引用
        while (true) {
            System.gc();
            Reference<? extends OneObject> poll = QUEUE.poll();
            if (poll != null) {
                System.out.println("隊列裏找到objectPr啦" + poll);
                break;
            }
        }
    }
}

執行代碼,可以看到以下輸出:

虛引用objectPr的地址:java.lang.ref.PhantomReference@72ea2f77
虛引用objectPr關聯的oneObject對象的地址:null
啊哦~OneObject被回收了
隊列裏找到objectPr啦java.lang.ref.PhantomReference@72ea2f77
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章