深入理解Java的4種引用類型

簡介

首先大家應(yīng)該都知道Java從1.2起提供了四種引用類型,他們分別是其引用(StrongReference),軟引用(SoftReference),弱引用(WeakReference)以及PhantomReference(虛引用),他們被GC回收的可能性從大到小排列。如下圖

可以看到Reference是繼承自O(shè)bject,而又有三個直接的子類,就是我們要介紹的幾個類了。另外還有一個ReferenceQueue我想大家就不一定知道了

他是用來的保存即將釋放的引用對象,具體用戶下面有實例代碼

下面就來分別介紹每個引用,并附上測試代碼,方便大家的深入理解

StrongReference

就可以他的名字一樣,任何時候GC是不能回收他的,哪怕內(nèi)存不足時,系統(tǒng)會直接拋出異常OutOfMemoryError,也不會去回收,首先要說明的是java中默認(rèn)就是強引用,比如如下代碼:

Persion p = new Persion();

其中p就是一個強引用,任何時候都不會被gc回收

/**
 * 測試強類型,可以看到我們調(diào)用多次gc他還是沒有回收
 */
private static void testStrongReference() {
    Person jack = new Person("Jack");
    System.gc();
    System.gc();
    System.out.println(jack);
}

SoftReference

軟引用他的特點是當(dāng)內(nèi)存足夠時不會回收這種引用類型的對象,只有當(dāng)內(nèi)存不夠用時才會回收,這種特點很適合最一些緩存,比如android中圖片的緩存

/**
 * 測試軟類型引用
 */
private static void testSoftReference() {
    Person jack = new Person("Jack");
    SoftReference<Person> personSoftReference = new SoftReference<>(jack);
    System.out.println(personSoftReference.get());
    jack = null;
    System.gc();
    System.gc();
    System.out.println(personSoftReference.get());
}

輸出:

Person{name='Jack'}
Person{name='Jack'}

可以看到雖然我們手動調(diào)用了GC但是引用類型沒有被回收

WeakReference

虛引用的特點是只要GC一運行就會把給回收了,個人感覺沒多大用處,因為只要GC一運行他就會被回收了‘

private static void testWeakReference() {
    Person jack = new Person("Jack");
    WeakReference<Person> personSoftReference = new WeakReference<Person>(jack);
    System.out.println(personSoftReference.get());
    System.gc();
    System.gc();
    System.out.println(personSoftReference.get());
}

我們運行代碼發(fā)現(xiàn)對象還是沒被回收,因為我們雖然調(diào)用了GC,這個方法只是通知他執(zhí)行,但是什么執(zhí)行還得看他心情。當(dāng)然上面的代碼有個很重要的原因是Jack對象他是強引用,所以在怎么調(diào)用GC他還是不會回收的,為啥呢?因為jack這個變量他還強引用著對象,我們給他置空,在調(diào)用GC他就回收了,如下代碼:

private static void testWeakReference() {
    Person jack = new Person("Jack");
    WeakReference<Person> personSoftReference = new WeakReference<Person>(jack);
    System.out.println(personSoftReference.get());
    jack = null;
    System.gc();
    System.gc();
    System.out.println(personSoftReference.get());
}

輸出:

Person{name='Jack'}
null

可以看到結(jié)果正如我們所料

PhantomReference

虛引用就相應(yīng)沒引用似得,主要以創(chuàng)建這種類型的引用,那么他所引用類型就回收了

private static void testPhantomReference() {
    Person jack = new Person("Jack");
    ReferenceQueue<Person> personReferenceQueue = new ReferenceQueue<>();
    PhantomReference<Person> personSoftReference = new PhantomReference<Person>(jack,personReferenceQueue);
    System.out.println(personSoftReference.get());
//        jack = null;
//        System.gc();
//        System.gc();
    System.out.println(personSoftReference.get());
}

輸出:

null
null

可以看到只要一創(chuàng)建完就直接給回收了

ReferenceQueue

他是一個引用對列,可以監(jiān)控被回收的Reference對象,具體的就是當(dāng)一個引用對象他所引用的值被回收了,那么系統(tǒng)就會把這個引用添加到這個對列里面,如下代碼:

private static void testReferenceQueue() {
    Person jack = new Person("Jack");
    ReferenceQueue<Person> personReferenceQueue = new ReferenceQueue<>();
    WeakReference<Person> personSoftReference = new WeakReference<Person>(jack, personReferenceQueue);
    jack = null;
    System.gc();
    System.out.println(personSoftReference.get());
}

當(dāng)personSoftReference所引用的Jack對象回收了,系統(tǒng)就會把personSoftReference對象添加到personReferenceQueue里,具體有什么作用呢,想象下這種情景,一個hashMap他的key是一個byte[]數(shù)組,我們需要創(chuàng)建一萬個這樣的key將他添加到hashMap,這是如果機器的內(nèi)存小那么他就可以內(nèi)存不足,但是當(dāng)我們用一個引用來包裹這個key,然后在添加到hashMap就不會了:

private static void testReferenceQueue1() {
    ReferenceQueue<Object> weakReferenceReferenceQueue = new ReferenceQueue<>();

    int M1 = 1024;
    Object o = new Object();
    Map<WeakReference<byte[]>, Object> map = new HashMap<>();

    //創(chuàng)建一個線程監(jiān)聽回收的對象
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                int cnt = 0;
                WeakReference<byte[]> k;
                while((k = (WeakReference) weakReferenceReferenceQueue.remove()) != null) {
                    System.out.println((cnt++) + "recycle:" + k+","+k.get());
                    System.out.println("map.size:" + map.size());
                }

            } catch(InterruptedException e) {
                //結(jié)束循環(huán)

            }
        }
    }).start();

    for (int i = 0; i < 10000; i++) {
        byte[] bytes = new byte[M1];
        map.put(new WeakReference<>(bytes,weakReferenceReferenceQueue),o);
    }
    System.gc();
    System.out.println("map.size:" + map.size());
}

輸出:

...
9997recycle:java.lang.ref.WeakReference@2a62b5bc,null
map.size:10000
9998recycle:java.lang.ref.WeakReference@27e47833,null
map.size:10000
9999recycle:java.lang.ref.WeakReference@18e8473e,null
map.size:10000

可以看到他回收了這么多對象,因為我們調(diào)用get()時返回的為null,但是問題來了,這時候map的size還是10000,這不是我們期望的,因為他所引用的對象都被回首了,那這個map里面的可以相當(dāng)于失效了,我們希望的是把reference對象也回收了,意思是map的size變成真實可用對象的size

private static void testReferenceQueue2() {
    ReferenceQueue<Object> weakReferenceReferenceQueue = new ReferenceQueue<>();

    int M1 = 1024;
    Object o = new Object();
    Map<WeakReference<byte[]>, Object> map = new HashMap<>();

    //創(chuàng)建一個線程監(jiān)聽回收的對象
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                int cnt = 0;
                WeakReference<byte[]> k;
                while ((k = (WeakReference) weakReferenceReferenceQueue.remove()) != null) {
                    map.remove(k); //在這里我們移除被回收對象的引用
                    System.out.println((cnt++) + "recycle:" + k);
                    System.out.println("map.size:" + map.size());
                }


            } catch (InterruptedException e) {
                //結(jié)束循環(huán)

            }
        }
    }).start();

    for (int i = 0; i < 10000; i++) {
        byte[] bytes = new byte[M1];
        map.put(new WeakReference<>(bytes, weakReferenceReferenceQueue), o);
    }
    System.gc();
    System.out.println("map.size:" + map.size());
}

輸出:

9996recycle:java.lang.ref.WeakReference@1608bcbd
map.size:3
9997recycle:java.lang.ref.WeakReference@777c9dc9
map.size:2
9998recycle:java.lang.ref.WeakReference@535779e4
map.size:1
9999recycle:java.lang.ref.WeakReference@6f6745d6
map.size:0

可以看見被回收了的對象,我們將它的key也從map里面移除了

WeakHashMap

他的效果是和上面差不多,當(dāng)一個key的引用對象被回收時他會自動的移除這個key

我們將下表是偶數(shù)的值裝到了list,這樣做是不讓他回收這些對象

private static void testWeakHashMap() {
    ReferenceQueue<Object> weakReferenceReferenceQueue = new ReferenceQueue<>();

    int M1 = 1024;
    Object o = new Object();
    Map<byte[], Object> map = new WeakHashMap<>();

    ArrayList<byte[]> bytes1 = new ArrayList<>();
    for (int i = 0; i < 10000; i++) {
        byte[] bytes = new byte[M1];
        if (i%2==0){
            bytes1.add(bytes);
        }
        map.put(bytes, o);
        bytes  = null;
    }
    System.gc();
    System.out.println("map.size:" + map.size());
}

輸出:

map.size:5000

可以看到正如我們上面說的,他會移除哪些被回收的key。

參考:http://www.iflym.com/index.php/java-programe/201407140001.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,652評論 18 399
  • 從三月份找實習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時芥藍閱讀 42,791評論 11 349
  • 1.什么是垃圾回收? 垃圾回收(Garbage Collection)是Java虛擬機(JVM)垃圾回收器提供...
    簡欲明心閱讀 90,359評論 17 311
  • 通過這篇文章你能知道的問題: 1.如何判斷對象是活著還是死去? 2.在Java語言中,可作為GCRoots的對象有...
    beneke閱讀 1,429評論 0 1
  • 和一個人在一起后,慢慢變得越來越像他。在一起的時候,會多看他喜歡看的電影,常吃他愛吃的食物,自己喜歡的音樂因為他的...
    令狐大俠turbo閱讀 210評論 0 0

友情鏈接更多精彩內(nèi)容