1. 程式人生 > >Java中的強引用、軟引用、弱引用和虛引用及其例項

Java中的強引用、軟引用、弱引用和虛引用及其例項

在《深入理解Java虛擬機器(第二版)》3.2.3節:再談引用 中,介紹了Java中的幾種引用:

在JDK 1.2以前,Java中的引用的定義很傳統:如果reference型別的資料中儲存的數值代表的是另外一塊記憶體的起始地址,就稱這塊記憶體代表著一個引用。 這種定義很純粹,但是太過狹隘,一個物件在這種定義下只有被引用或者沒有被引用兩種狀態,對於如何描述一些“食之無味,棄之可惜”的物件就顯得無能為力。 我們希望能描述這樣一類物件:當記憶體空間還足夠時,則能保留在記憶體之中;如果記憶體空間在進行垃圾收集後還是非常緊張,則可以拋棄這些物件。 很多系統的快取功能都符合這樣的應用場景。

在JDK 1.2之後,Java對引用的概念進行了擴充,將引用分為強引用(StrongReference)、 軟引用(Soft Reference)、 弱引用(Weak Reference)、 虛引用(PhantomReference)4種,這4種引用強度依次逐漸減弱。

強引用就是指在程式程式碼之中普遍存在的,類似“Object obj=new Object()”這類的引用,只要強引用還存在,垃圾收集器永遠不會回收掉被引用的物件。

軟引用是用來描述一些還有用但並非必需的物件。 對於軟引用關聯著的物件,在系統將要發生記憶體溢位異常之前,將會把這些物件列進回收範圍之中進行第二次回收。 如果這次回收還沒有足夠的記憶體,才會丟擲記憶體溢位異常。 在JDK 1.2之後,提供了SoftReference類來實現軟引用。

弱引用也是用來描述非必需物件的,但是它的強度比軟引用更弱一些,被弱引用關聯的物件只能生存到下一次垃圾收集發生之前。 當垃圾收集器工作時,無論當前記憶體是否足夠,都會回收掉只被弱引用關聯的物件。 在JDK 1.2之後,提供了WeakReference類來實現弱引用。

虛引用也稱為幽靈引用或者幻影引用,它是最弱的一種引用關係。 一個物件是否有虛引用的存在,完全不會對其生存時間構成影響,也無法通過虛引用來取得一個物件例項。 為一個物件設定虛引用關聯的唯一目的就是能在這個物件被收集器回收時收到一個系統通知。 在JDK 1.2之後,提供了PhantomReference類來實現虛引用。

在書中沒有給出示例程式碼,這裡給出幾種引用的示例的程式碼。

強引用

public class StrongReferenceTest {
    public static void main(String[] args) throws InterruptedException {
        StrongReferenceTest object
= new StrongReferenceTest(); System.gc(); TimeUnit.SECONDS.sleep(1);//暫停一秒鐘 System.out.println(object == null);//false } }

結果輸出false,表明沒有被垃圾回收。

軟引用

/**
 * java -Xms10m -Xmx10m SoftReferenceTest 
 */
public class SoftReferenceTest {

    static class HeapObject {
        byte[] bs = new byte[1024 * 1024];
    }

    public static void main(String[] args) {
        SoftReference<HeapObject> softReference = new SoftReference<>(new HeapObject());

        List<HeapObject> list = new ArrayList<>();

        while (true) {
            if (softReference.get() != null) {
                list.add(new HeapObject());
                System.out.println("list.add");
            } else {
                System.out.println("---------軟引用已被回收---------");
                break;
            }
            System.gc();
        }
    }
}

使用引用佇列

/**
 * java -Xms10m -Xmx10m SoftReferenceTest
 */
public class SoftReferenceQueueTest {

    static class HeapObject {
        byte[] bs = new byte[1024 * 1024];
    }

    public static void main(String[] args) {
        ReferenceQueue<HeapObject> queue=new ReferenceQueue<>();
        SoftReference<HeapObject> softReference = new SoftReference<>(new HeapObject(),queue);

        List<HeapObject> list = new ArrayList<>();

        while (true) {
            if (softReference.get() != null) {
                list.add(new HeapObject());
                System.out.println("list.add");
            } else {
                System.out.println("---------軟引用已被回收---------");
                break;
            }
            System.gc();
        }
        Reference<? extends  HeapObject> pollRef = queue.poll();
        while (pollRef != null) {
            System.out.println(pollRef);
            System.out.println(pollRef.get());
            pollRef = queue.poll();
        }

    }
}

輸出:

list.add
list.add
list.add
//若干個...list.add
---------軟引用已被回收---------
java.lang.ref.SoftReference@7ea987ac
null

那麼當這個SoftReference所軟引用的HeapObject 被垃圾收集器回收的同時,SoftReference物件被列入ReferenceQueue。也就是說,ReferenceQueue中儲存的物件是Reference物件,而且是已經失去了它所軟引用的物件(HeapObject )的Reference物件。

弱引用

當垃圾收集器工作時,無論當前記憶體是否足夠,都會回收掉只被弱引用關聯的物件

/**
 * jdk 1.8
 */
public class WeakReferenceTest {
    static class TestObject{

    }

    public static void main(String[] args) throws InterruptedException {
        WeakReference<TestObject> weakReference=new WeakReference<>(new TestObject());

        System.out.println(weakReference.get() == null);//false

        System.gc();
        TimeUnit.SECONDS.sleep(1);//暫停一秒鐘

        System.out.println(weakReference.get() == null);//true
    }
}

虛引用

public class PhantomReferenceTest {
    static class TestObject {

    }

    public static void main(String[] args) throws InterruptedException {
        ReferenceQueue<TestObject> queue = new ReferenceQueue<>();
        PhantomReference<TestObject> phantomReference = new PhantomReference<>(new TestObject(), queue);

        System.out.println(phantomReference.get() == null);//true


    }
}

無法獲取phantomReference所引用的物件