天天看點

Java Reference引用 強引用 軟引用 弱引用 虛引用

1,強引用:

package com.myjava.reference;

/**

 * ①強引用不會被垃圾回收器自動回收

 * ②當記憶體空間不足時,Java虛拟機甯可抛出OutOfMemoryError錯誤,也不會随意回收強引用對象來解決記憶體不足問題

 * @author Administrator

 *

 */

public class TestStrongReference {

public static void main(String[] args) {

Object ref = new Object();

Object strongRef = ref;

System.gc();

System.out.println(strongRef!=null?"StrongRef not gc":"StrongRef is gc");

}

-------------------------------------------------------------------------------------------------------------------------------

2,軟引用;

import java.lang.ref.SoftReference;

 * ①,softReference 軟引用在記憶體充足時不會被GC,

 * ②,當記憶體空間不足時就會回收這些對象的記憶體

 * ③,适合做高速通路,如cache--Android開發中

public class TestSoftReference {

String str = "test soft GC";

SoftReference<String> softReference = new SoftReference<String>(str);

str = null;

System.out.println(softReference.get() != null ? "softReference not gc"

: "softReference is gc");

----------------------------------------------------------------------------------------------------------

弱引用:

import java.lang.ref.WeakReference;

 * ①,不管目前記憶體空間足夠與否,都會回收它的記憶體。

 * ②,結合ReferenceQueue可以讓程式在第一時間得到referent被回收的事件

 * ③,一般用來防止記憶體洩漏,要保證記憶體被VM回收

public class TestWeakReference {

String   str  =   "test weakReference " ;  

WeakReference < String >   weakReference  =  new  WeakReference <String>(str);  

str = null;  

System.gc();  

System.out.println(weakReference.get()!= null? "weakReference not gc": "weakReference is gc");  

​​javascript:void(0)​​

​​http://zhangjunhd.blog.51cto.com/113473/53092/​​

-----------------------------------------------------------------------------------------------------------------

4,虛引用

import java.lang.ref.PhantomReference;

import java.lang.ref.Reference;

import java.lang.ref.ReferenceQueue;

import java.util.HashMap;

public class TestPhantomReference {

    public static void main(String[] args) {

        ReferenceQueue referenceQueue = new ReferenceQueue();

        Object object = new Object() {

            public String toString() {

                return "Referenced Object";

            }

        };

        Object data = new Object() {

                return "Data";

        HashMap map = new HashMap();

        Reference reference = null;

        System.out.println("Testing PhantomReference.");

        reference = new PhantomReference(object, referenceQueue);

        map.put(reference, data);

        System.out.println(reference.get()); // null

        System.out.println(map.get(reference)); // Data

        System.out.println(reference.isEnqueued()); // false

        System.gc();

        object = null;

        data = null;

        System.out.println(reference.isEnqueued()); // true, because object has been reclaimed.

    }

Strong Reference, 強引用,即java标準的引用方式,表示GC從 Root Set 開始向下掃描,可以找到對應的 Strong Reference。

Referent,被包裝為 Weak, Soft, Phantom Reference的對象引用稱之為 referent。後面的内容會多次提到這個名詞。

Weak Reference, 弱引用。當一個referent,在運作時沒有同時被強,軟引用,隻被Weak Reference自身引用,且Weak Reference從 Root Set 可達,則該referent會被GC回收。

WR的作用,一般是為referent提供一個被回收的憑據,結合ReferenceQueue可以讓程式在第一時間得到referent被回收的事件,進而做一些額外的clean操作。(如果對ReferenceQueue作用和回調感興趣,可以先看最下面的 ReferenceQueue 簡介)

Soft Reference, 軟引用。它是除strong外,生命周期最長的一種 Reference,隻有當JVM Heap中充滿Strong References, Full GC無法為heap騰出更多空間而即将抛出OOM時,SoftReferences會被GC回收。

SR的作用一般是用作不限大小的 cache(無需remove)。

比如将其 Soft Reference 無限地放入 hashmap 中,而不關心hashmap内的對象數量是否會撐爆heap,也不需要手動 remove。

當Heap容量達到OOM觸發條件時,VM會自動回收該map裡的所有SoftReferences.

Phanton Reference, 是一種特殊的Reference,正如他的名字所表達的,幻影引用,他可以像幻影一樣附着在referent上。

當GC在周遊引用關系時,如果發現被phantom reference包裝過的referent不存在strong, weak, soft引用時(就是除phantom外沒有任何引用,幻影的由來),GC會将 phantom reference 放入 Reference queue。以便程式在另一邊通過queue的remove/poll方法,感覺referent被GC回收的事件。(如果對 ReferenceQueue作用和回調感興趣,可以先看最下面 ReferenceQueue 簡介)

另外,我們知道,GC在回收對象前會先調用對象自身的finalize()方法,如果它有實作的話,然後再清掉記憶體。而Phantom Reference的回調(enqueue)是在對象的finalize後,回收前觸發。這跟 WeakReference不一樣。WR是在回收後才通知的。在這個特殊的階段可以做一些特殊的clean操作。

為什麼 Phantom Reference 的get總是傳回null?

因為phantom reference想做到幻影(除自身外,不跟其他任何引用有關聯),是以不允許程式能通過自身的get方法得到referent,而破壞幻影的初衷。

執行個體代碼

package com.kenwublog.reference;

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.HashMap;

public class PhantomReferenceTest {

    public static void main(String[] args) {
        ReferenceQueue referenceQueue = new ReferenceQueue();
        Object object = new Object() {
            public String toString() {
                return "Referenced Object";
            }
        };

        Object data = new Object() {
            public String toString() {
                return "Data";
            }
        };

        HashMap map = new HashMap();
        Reference reference = null;
        System.out.println("Testing PhantomReference.");
        reference = new PhantomReference(object, referenceQueue);

        map.put(reference, data);

        System.out.println(reference.get()); // null
        System.out.println(map.get(reference)); // Data
        System.out.println(reference.isEnqueued()); // false

        System.gc();
        System.out.println(reference.get()); // null
        System.out.println(map.get(reference)); // Data
        System.out.println(reference.isEnqueued()); // false

        object = null;
        data = null;

        System.gc();
        System.out.println(reference.get()); // null
        System.out.println(map.get(reference)); // Data
        System.out.println(reference.isEnqueued()); // true, because object has been reclaimed.
    }

}      

ReferenceQueue,一種當 weak, soft, phantom的referent被GC回收後,提供事件回調的接口。需要在執行個體化三大reference時,通過構造函數傳入,phantom reference是強制需要傳入的,weak和soft可不傳。

回調過程:

GC回收referent後(phantom是在回收前,finalize後),将reference enqueue到RQ中,程式通過調用RQ的remove方法來感覺reference被GC回收的事件。

remove方法是阻塞的,當沒有referent被回收時(GC未調用enqueue),remove方法會一直挂起線程,當有referent被回收時,該方法傳回 referent對應的reference對象。

同樣,RQ也提供了一個非阻塞的方法 poll,但這樣就做不到實時回調了。

執行個體

package com.kenwublog.reference;

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashMap;

public class ReferenceQueueTest {

    public static void main(String[] args) {
        final ReferenceQueue q = new ReferenceQueue();
        String str = new String("AK47");
        WeakReference wr = new WeakReference(str, q);

        Thread t = new Thread(){
            @Override
            public void run() {
                try {
                    Reference reference = q.remove();
                    System.out.println(reference + " event fired.");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        t.setDaemon(true);
        t.start();
        System.out.println("Reference Queue is listening.");

        str = null; // clear strong reference
        System.out.println("Ready to gc");
        System.gc();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("wr.get: " + wr.get());
    }

}