《Java原始碼分析》:ReferenceQueue、Reference及其子類

NO IMAGE

《Java原始碼分析》:ReferenceQueue、Reference及其子類

在看完WeakHashMap原始碼之後,看了有關於講解WeakHashMap的些許部落格,發現了幾個比較有意思的類:Reference、Reference子類(SoftReference、WeakReference、PhantomReference)以及ReferenceQueue。

以前自己只是知道這些類的存在,在看WeakHashMap原始碼之前並不知道他們的用途,因此,藉此機會自己就想對這幾個類瞭解下,查了網上相關資料、看了原始碼和相關API文件,都沒能完全的理解這些類以及這些類和垃圾回收之間的互動,只是有一個小小的認識。下面就來一一進行說明,若有錯誤,請指出並諒解。

1、ReferenceQueue類

由於Reference類中有關於ReferenceQueue的引用,因此,先對ReferenceQueue進行介紹。

原始碼中對該類的說明摘入如下:

Reference queues, to which registered reference objects are appended by the
garbage collector after the appropriate reachability changes are detected.

中文意思為:Reference queues,在適當的時候檢測到物件的可達性發生改變後,垃圾回收器就將已註冊的引用物件新增到此佇列中。

此類中的方法比較少,只有:enqueue(Reference

    //出隊標識
static ReferenceQueue<Object> NULL = new Null<>();
//出隊標識
static ReferenceQueue<Object> ENQUEUED = new Null<>();
//鎖物件
static private class Lock { };
private Lock lock = new Lock();
//連結串列的頭結點
private volatile Reference<? extends T> head = null;
//佇列的大小
private long queueLength = 0;
boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */
synchronized (lock) {
// Check that since getting the lock this reference hasn't already been
// enqueued (and even then removed)
ReferenceQueue<?> queue = r.queue;
if ((queue == NULL) || (queue == ENQUEUED)) {
return false;
}
assert queue == this;
r.queue = ENQUEUED;//入隊標識
r.next = (head == null) ? r : head;
head = r;
queueLength  ;
if (r instanceof FinalReference) {
sun.misc.VM.addFinalRefCount(1);
}
lock.notifyAll();
return true;
}
}

2、poll()方法

實現思路也相當簡單,就是判斷佇列中是否為空,如果不為空,則取出連結串列中head位置的元素即可,出隊的Reference物件要加上出隊標識NULL。

原始碼如下:

    public Reference<? extends T> poll() {
if (head == null)
return null;
synchronized (lock) {
return reallyPoll();
}
}
@SuppressWarnings("unchecked")
private Reference<? extends T> reallyPoll() {       /* Must hold lock */
Reference<? extends T> r = head;
if (r != null) {
head = (r.next == r) ?
null :
r.next; // Unchecked due to the next field having a raw type in Reference
r.queue = NULL;//出隊標識
r.next = r;//出隊的Reference物件的next指向自己
queueLength--;
if (r instanceof FinalReference) {
sun.misc.VM.addFinalRefCount(-1);
}
return r;
}
return null;
}

remove方法這裡就不再介紹,以上就是關於ReferenceQueue的一個簡單的介紹。

2.Reference類

2.1、介紹

在Reference類原始碼的開頭對此類有一個說明,摘入如下:

Abstract base class for reference objects. This class defines the
operations common to all reference objects. Because reference objects are
implemented in close cooperation with the garbage collector, this class may
not be subclassed directly.

比較好理解,中文翻譯為:這是引用物件的抽象基類,這個類中定義了所有引用物件的常用操作。由於引用物件是通過與垃圾回收器密切合作來實現的,因此,不能直接為此類建立子類。

以上就是原始碼中對此類的一個說明,我們可能獲得到的有用資訊為:Reference類是基類且和GC是密切相關的。

2.2、Reference類的4中狀態

在原始碼中,我們可以瞭解到,Reference有4種狀態:

1)、Active

原始碼中對Active狀態說明如下:

Active: Subject to special treatment by the garbage collector. Some
time after the collector detects that the reachability of the referent has changed to the appropriate state, it changes the instance’s state to either Pending or Inactive, depending upon
whether or not the instance was registered with a queue when it was
created. In the former case it also adds the instance to the
pending-Reference list. Newly-created instances are Active.

翻譯為:Active狀態的Reference會受到GC的特別關注,當GC察覺到引用的可達性變化為其它的狀態之後,它的狀態將變化為Pending或Inactive,到底轉化為Pending狀態還是Inactive狀態取決於此Reference物件建立時是否註冊了queue.如果註冊了queue,則將新增此例項到pending-Reference list中。 新建立的Reference例項的狀態是Active。

每當我自己翻譯的時候,都感覺翻譯技術書籍的人真心不容易,很多東西我們自己知道是什麼意思,但是當翻譯過來的時候總是感覺沒有描述清楚

2)、Pending

原始碼中對此狀態給出的解釋為:

Pending: An element of the pending-Reference list, waiting to be
enqueued by the Reference-handler thread. Unregistered instances
are never in this state.

翻譯為:在pending-Reference list中等待著被Reference-handler 執行緒入佇列queue中的元素就處於這個狀態。沒有註冊queue的例項是永遠不可能到達這一狀態。

3)、Enqueued

原始碼中對此狀態給出的解釋為:

Enqueued: An element of the queue with which the instance was
registered when it was created. When an instance is removed from
its ReferenceQueue, it is made Inactive. Unregistered instances are
never in this state.

翻譯為:當例項被移動到ReferenceQueue中時,Reference的狀態為Inactive。沒有註冊ReferenceQueue的不可能到達這一狀態的。

4)、Inactive

原始碼中對此狀態給出的解釋為:

Inactive: Nothing more to do. Once an instance becomes Inactive its
state will never change again.

翻譯為:一旦一個例項變為Inactive,則這個狀態永遠都不會再被改變。

以上就是Reference的4中狀態的一個說明。

2.3 Reference屬性的介紹

Reference屬性的介紹

在Reference中,從資料結構上看,Reference連結串列結構內部主要有:

    private T referent;         /* Treated specially by GC */
Reference next;//指向下一個

另一個相當重要的屬性為:

  volatile ReferenceQueue<? super T> queue;

這個queue是通過建構函式傳入的,表示建立一個Reference時,要將其註冊到那個queue上。

Queue的另外一個作用是可以區分不同狀態的Reference。Reference有4中狀態。分別為:Active、Pending、Enqueued和Inactive狀態,關於這4中狀態的具體含義在本文上面已經進行了簡單的介紹。而這4中狀態所對應的Queue如下:

1、Active

Reference類中原始碼給出的結果為:

    Active: queue = ReferenceQueue with which instance is registered, or
ReferenceQueue.NULL if it was not registered with a queue; next =
null

至於為什麼是這樣的,我們可以從Reference類中的建構函式分析得到。

Reference類中的建構函式為:

    /* -- Constructors -- */
Reference(T referent) {
this(referent, null);
}
Reference(T referent, ReferenceQueue<? super T> queue) {
this.referent = referent;
this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}

由於Newly-created instances are Active.也就是利用構造出來的Reference例項都處於這樣一個狀態,而從建構函式可以看出,得到如下的結論:queue = ReferenceQueue with which instance is registered, or ReferenceQueue.NULL if it was not registered with a queue; next = null。

2、Pending

Reference類中原始碼給出的結果為:

     Pending: queue = ReferenceQueue with which instance is registered;
next = this

Pending狀態的Reference例項的queue = ReferenceQueue with which instance is registered;這個我們都比較好理解,但是next = this ,這個目前我就找不到根源來分析得出了。

3、Enqueued

Reference類中原始碼給出的結果為:

     Enqueued: queue = ReferenceQueue.ENQUEUED; next = Following instance
in queue, or this if at end of list.

而為什麼Enqueued狀態的Reference例項的queue為ReferenceQueue.ENQUEUED以及next的值,我們是可以從ReferenceQueue中分析得到的??

分析如下:從上面的介紹可知,當Reference例項入佇列queue時,Reference狀態就變為了Enqueued,而從ReferenceQueue的enqueue方法的原始碼中有這樣兩行程式碼r.queue = ENQUEUED;r.next = (head == null) ? r : head;,r.queue = ENQUEUED實現了對於入隊的Reference的queue都進行了入隊標識;這行程式碼r.next = (head == null) ? r : head;實現入隊的Reference加在了連結串列的head位置

4、Inactive

Reference類中原始碼給出的結果為:

  Inactive: queue = ReferenceQueue.NULL; next = this.

而為什麼Inactive狀態的Reference例項的queue為ReferenceQueue.NULL以及next = this,我們是可以從ReferenceQueue中分析得到的??

分析如下:從上面的介紹可知,當Reference例項出佇列queue時,Reference狀態就變為了Inactive,而從ReferenceQueue的poll方法的原始碼中有這樣兩行程式碼r.queue = NULL;r.next = r;,r.queue = NULL實現了對於出隊的Reference的queue都進行了出隊標識;這行程式碼r.next = r;實現出隊的Reference物件的next指向自己

在Reference原始碼中,還有這樣兩個屬性:

1)、Reference類中的pending屬性

這個物件,定義為private的,但是全域性沒有任何地方給出他賦值的地方,根據下面的註釋,我們可以瞭解到這個變數是和垃圾回收打交道的。

    /* List of References waiting to be enqueued.  The collector adds
* References to this list, while the Reference-handler thread removes
* them.  This list is protected by the above lock object. The
* list uses the discovered field to link its elements.
*/
private static Reference<Object> pending = null;

2)、Reference類中的discovered屬性

與pending屬性一樣,同為private且上下文都沒有任何地方使用它,在註釋中,我們可以看到這個變數也是和垃圾回收打交道的。

    /* When active:   next element in a discovered reference list maintained by GC (or this if last)
*     pending:   next element in the pending list (or null if last)
*   otherwise:   NULL
*/
transient private Reference<T> discovered;  /* used by VM */

2.4、ReferenceHandler執行緒

下面看Reference類中的ReferenceHandler執行緒

原始碼如下,

從原始碼中可以看出,這個執行緒在Reference類的static構造塊中啟動,並且被設定為最高優先順序和daemon狀態。此執行緒要做的事情就是不斷的的檢查pending是否為null,如果pending不為null,則將pending進行enqueue,否則執行緒進行wait狀態。

    private static class ReferenceHandler extends Thread {
ReferenceHandler(ThreadGroup g, String name) {
super(g, name);
}
public void run() {
for (;;) {
Reference<Object> r;
synchronized (lock) {
if (pending != null) {
r = pending;
pending = r.discovered;
r.discovered = null;
} else {
// The waiting on the lock may cause an OOME because it may try to allocate
// exception objects, so also catch OOME here to avoid silent exit of the
// reference handler thread.
//
// Explicitly define the order of the two exceptions we catch here
// when waiting for the lock.
//
// We do not want to try to potentially load the InterruptedException class
// (which would be done if this was its first use, and InterruptedException
// were checked first) in this situation.
//
// This may lead to the VM not ever trying to load the InterruptedException
// class again.
try {
try {
lock.wait();
} catch (OutOfMemoryError x) { }
} catch (InterruptedException x) { }
continue;
}
}
// Fast path for cleaners
if (r instanceof Cleaner) {
((Cleaner)r).clean();
continue;
}
ReferenceQueue<Object> q = r.queue;
if (q != ReferenceQueue.NULL) q.enqueue(r);
}
}
}
static {
ThreadGroup tg = Thread.currentThread().getThreadGroup();
for (ThreadGroup tgn = tg;
tgn != null;
tg = tgn, tgn = tg.getParent());
Thread handler = new ReferenceHandler(tg, "Reference Handler");
/* If there were a special system-only priority greater than
* MAX_PRIORITY, it would be used here
*/
handler.setPriority(Thread.MAX_PRIORITY);
handler.setDaemon(true);
handler.start();
}

有了以上的基礎,我們來看一個Reference的應用

    /*
* 建立一個WeakReference,並且將其referent改變
* */
public class TestReference2 {
public static void main(String[] args) {
Object o = new Object();
WeakReference<Object> wr = new WeakReference<Object>(o);
System.out.println(wr.get());//[email protected]
o = null;
System.gc();
System.out.println(wr.get());//null
}
}

上面的程式碼我們都比較容易的知道執行結果。但是至於內部實現的原因,是這樣的。

由於pending是由JVM來賦值的,當Reference內部的referent物件的可達狀態發生改變時,JVM會將Reference物件放入到pending連結串列中。因此,在例子中的程式碼o = null這一句,它使得o物件滿足垃圾回收的條件,並且在後邊顯示呼叫了System.gc(),垃圾收集進行的時候會標記WeakReference的referent的物件o為不可達(使得wr.get()==null),並且通過賦值給pending,觸發ReferenceHandler執行緒來處理pending.ReferenceHandler執行緒要做的是將pending物件enqueue。但在這個程式中我們從建構函式傳入的為null,即實際使用的是ReferenceQueue.NULL,ReferenceHandler執行緒判斷queue如果為ReferenceQueue.NULL則不進行enqueue,如果不是,則進行enqueue操作。

ReferenceQueue.NULL相當於我們提供了一個空的Queue去監聽垃圾回收器給我們的反饋,並且對這種反饋不做任何處理。要處理反饋,則必須要提供一個非ReferenceQueue.NULL的queue。WeakHashMap類中提供的就是一個由意義的ReferenceQueue,非ReferenceQueue.NULL。

以上就是關於Reference類的一個介紹,可能會比較不好理解,因為確實不怎麼好理解。

4種引用

我們都知道在Java中有4種引用,這四種引用從高到低分別為:

1)、StrongReference

這個引用在Java中沒有相應的類與之對應,但是強引用比較普遍,例如:Object obj = new Object();這裡的obj就是要給強引用,如果一個物件具有強引用,則垃圾回收器始終不會回收此物件。當記憶體不足時,JVM情願丟擲OOM異常使程式異常終止也不會靠回收強引用的物件來解決記憶體不足的問題。

2)、SoftReference

如果一個物件只有軟引用,則在記憶體充足的情況下是不會回收此物件的,但是,在內部不足即將要丟擲OOM異常時就會回收此物件來解決記憶體不足的問題。

    public class TestReference3 {
private static ReferenceQueue<Object> rq = new ReferenceQueue<Object>();
public static void main(String[] args){
Object obj = new Object();
SoftReference<Object> sf = new SoftReference(obj,rq);
System.out.println(sf.get()!=null);
System.gc();
obj = null;
System.out.println(sf.get()!=null);
}
}

執行結果均為:true。

這也就說明了當記憶體充足的時候一個物件只有軟引用也不會被JVM回收。

3)、WeakReference

WeakReference基本與SoftReference類似,只是回收的策略不同。

只要GC發現一個物件只有弱引用,則就會回收此弱引用物件。但是由於GC所在的執行緒優先順序比較低,不會立即發現所有弱引用物件並進行回收。只要GC對它所管轄的記憶體區域進行掃描時發現了弱引用物件就進行回收。

看一個例子:

    public class TestWeakReference {
private static ReferenceQueue<Object> rq = new ReferenceQueue<Object>();
public static void main(String[] args) {
Object obj = new Object();
WeakReference<Object> wr = new WeakReference(obj,rq);
System.out.println(wr.get()!=null);
obj = null;
System.gc();
System.out.println(wr.get()!=null);//false,這是因為WeakReference被回收
}
}

執行結果為: true 、false

在指向obj = null語句之前,Object物件有兩條引用路徑,其中一條為obj強引用型別,另一條為wr弱引用型別。此時無論如何也不會進行垃圾回收。當執行了obj = null.Object物件就只具有弱引用,並且我們進行了顯示的垃圾回收。因此此具有弱引用的物件就被GC給回收了。

4)、PhantomReference

PhantomReference,即虛引用,虛引用並不會影響物件的生命週期。虛引用的作用為:跟蹤垃圾回收器收集物件這一活動的情況。

當GC一旦發現了虛引用物件,則會將PhantomReference物件插入ReferenceQueue佇列,而此時PhantomReference物件並沒有被垃圾回收器回收,而是要等到ReferenceQueue被你真正的處理後才會被回收。

注意:PhantomReference必須要和ReferenceQueue聯合使用,SoftReference和WeakReference可以選擇和ReferenceQueue聯合使用也可以不選擇,這使他們的區別之一。

接下來看一個虛引用的例子。

    public class TestPhantomReference {
private static ReferenceQueue<Object> rq = new ReferenceQueue<Object>();
public static void main(String[] args){
Object obj = new Object();
PhantomReference<Object> pr = new PhantomReference<Object>(obj, rq);
System.out.println(pr.get());
obj = null;
System.gc();
System.out.println(pr.get());
Reference<Object> r = (Reference<Object>)rq.poll();
if(r!=null){
System.out.println("回收");
}
}
}

執行結果:null null 回收

根據上面的例子有兩點需要說明:

1)、PhantomReference的get方法無論在上面情況下都是返回null。這個在PhantomReference原始碼中可以看到。

2)在上面的程式碼中,如果obj被置為null,當GC發現虛引用,GC會將把PhantomReference物件pr加入到佇列ReferenceQueue中,注意此時pr所指向的物件並沒有被回收,在我們現實的呼叫了rq.poll()返回Reference物件之後當GC第二次發現虛引用,而此時JVM將虛引用pr插入到佇列rq會插入失敗,此時GC才會對虛引用物件進行回收。

下面對Reference的3個子類進行一個簡要的說明。

SoftReference類

在JDK文件對此類的介紹如下:

1)、軟引用物件,在響應記憶體需要時,由垃圾回收器決定是否清除此物件。軟引用物件最常用於實現記憶體敏感的快取。

2)、假定垃圾回收器確定在某一時間點某個物件是軟可到達物件。這時,它可以選擇自動清除針對該物件的所有軟引用,以及通過強引用鏈從其可以到達該物件的針對任何其他軟可到達物件的所有軟引用。在同一時間或晚些時候,它會將那些已經向引用佇列註冊的新清除的軟引用加入佇列。

3)、軟可到達物件的所有軟引用都要保證在虛擬機器丟擲 OutOfMemoryError 之前已經被清除。否則,清除軟引用的時間或者清除不同物件的一組此類引用的順序將不受任何約束。然而,虛擬機器實現不鼓勵清除最近訪問或使用過的軟引用。

4)、此類的直接例項可用於實現簡單快取;該類或其派生的子類還可用於更大型的資料結構,以實現更復雜的快取。只要軟引用的指示物件是強可到達物件,即正在實際使用的物件,就不會清除軟引用。例如,通過保持最近使用的項的強指示物件,並由垃圾回收器決定是否放棄剩餘的項,複雜的快取可以防止放棄最近使用的項。

總結:SoftReference是軟引用,只有在JVM在拋OOM異常時才會回收。其它情況下不會回收。

WeakReference類

在JDK文件對此類的介紹如下:

1)、弱引用物件,它們並不禁止其指示物件變得可終結,並被終結,然後被回收。弱引用最常用於實現規範化的對映。

2)、假定垃圾回收器確定在某一時間點上某個物件是弱可到達物件。這時,它將自動清除針對此物件的所有弱引用,以及通過強引用鏈和軟引用,可以從其到達該物件的針對任何其他弱可到達物件的所有弱引用。同時它將宣告所有以前的弱可到達物件為可終結的。在同一時間或晚些時候,它將那些已經向引用佇列註冊的新清除的弱引用加入佇列。

PhantomReference類

在JDK文件對此類的介紹如下:

1)、虛引用物件,在回收器確定其指示物件可另外回收之後,被加入佇列。虛引用最常見的用法是以某種可能比使用 Java 終結機制更靈活的方式來指派 pre-mortem 清除動作。

2)、如果垃圾回收器確定在某一特定時間點上虛引用的指示物件是虛可到達物件,那麼在那時或者在以後的某一時間,它會將該引用加入佇列。

3)、為了確保可回收的物件仍然保持原狀,虛引用的指示物件不能被獲取:虛引用的 get 方法總是返回 null。

4)、與軟引用和弱引用不同,虛引用在加入佇列時並沒有通過垃圾回收器自動清除。通過虛引用可到達的物件將仍然保持原狀,直到所有這類引用都被清除,或者它們都變得不可到達。

應用

最後,看一個在《Java程式設計思想》這本書上的一個例子

    public class References {
private static ReferenceQueue<VeryBig> rq = new ReferenceQueue<VeryBig>();
public static void checkQueue(){
Reference<? extends VeryBig> inq = rq.poll();
if(inq!=null){
System.out.println("In queue:" inq.getClass().getSimpleName());
}
}
public static void main(String[] args) {
int size = 10;
/*
* SoftReference:在記憶體不足時才會回收這樣軟引用物件
* */
LinkedList<SoftReference<VeryBig>> sa = new  LinkedList<SoftReference<VeryBig>>();
for(int i=0;i<size;i  ){
sa.add(new SoftReference(new VeryBig("Soft " i),rq));
System.out.println("Just created: " sa.getLast().get());
checkQueue();//一直為空
}
/*
* WeakReference:在GC發現只具有弱引用的物件會立即對其會回收
* */
LinkedList<WeakReference<VeryBig>> wa = new  LinkedList<WeakReference<VeryBig>>();
for(int i=0;i<size;i  ){
wa.add(new WeakReference(new VeryBig("Weak " i),rq));
System.out.println("Just created: " wa.getLast().get());
checkQueue();
}
SoftReference<VeryBig> sf = new SoftReference<VeryBig>(new VeryBig("Soft "));
WeakReference<VeryBig> wf = new WeakReference<VeryBig>(new VeryBig("Weak"));
System.gc();//顯示的進行垃圾回收,什麼時候執行就由JVM決定
LinkedList<PhantomReference<VeryBig>> pa = new  LinkedList<PhantomReference<VeryBig>>();
for(int i=0;i<size;i  ){
pa.add(new PhantomReference(new VeryBig("Phantom " i),rq));
System.out.println("Just created: " pa.getLast());
checkQueue();
}       
}
}
class VeryBig{
private static final int SIZE = 10000;
private long[] la = new long[SIZE];
private String ident;
public VeryBig(String id){
ident = id;
}
public String toString(){
return ident;
}
protected void finalize(){
System.out.println("Finalizing "  ident);
}
}

執行結果為:

    Just created: Soft 0
Just created: Soft 1
Just created: Soft 2
Just created: Soft 3
Just created: Soft 4
Just created: Soft 5
Just created: Soft 6
Just created: Soft 7
Just created: Soft 8
Just created: Soft 9
Just created: Weak 0
Just created: Weak 1
Just created: Weak 2
Just created: Weak 3
Just created: Weak 4
Just created: Weak 5
Just created: Weak 6
Just created: Weak 7
Just created: Weak 8
Just created: Weak 9
Just created: [email protected]
In queue:WeakReference
Just created: [email protected]
In queue:WeakReference
Just created: [email protected]
In queue:WeakReference
Just created: [email protected]
In queue:WeakReference
Just created: [email protected]
In queue:WeakReference
Just created: [email protected]
In queue:WeakReference
Just created: [email protected]
In queue:WeakReference
Just created: [email protected]
In queue:WeakReference
Just created: [email protected]
In queue:WeakReference
Just created: [email protected]
In queue:WeakReference

根據上面的知識點的介紹,理解這個程式應該不難。下面還是分析下:

首先對於下一段程式碼,由於是SoftReference引用,因此只有在記憶體不足時才會被回收。

            LinkedList<SoftReference<VeryBig>> sa = new  LinkedList<SoftReference<VeryBig>>();
for(int i=0;i<size;i  ){
sa.add(new SoftReference(new VeryBig("Soft " i),rq));
System.out.println("Just created: " sa.getLast().get());
checkQueue();//一直為空
}

對於下面這段程式碼,由於是WeakReference引用,因此當GC發現其就會被回收。由於這段程式碼最後一句進行了gc的顯示呼叫,因此這些具有弱引用的物件就都會被回收掉,在回收之前呼叫了物件的finalize的方法並將WeakReference加入的佇列Queue中。

            LinkedList<WeakReference<VeryBig>> wa = new  LinkedList<WeakReference<VeryBig>>();
for(int i=0;i<size;i  ){
wa.add(new WeakReference(new VeryBig("Weak " i),rq));
System.out.println("Just created: " wa.getLast().get());
checkQueue();
}
System.gc();//顯示的進行垃圾回收,什麼時候執行就由JVM決定

對於最後一段程式碼,在執行這段程式碼之前,如果呼叫System.gc()後JVM立即進行垃圾回收則佇列queue裝的是已經被回收的WeakReference引用,因此,在下段程式碼中的每個for迴圈中呼叫的checkQueue()函式就有輸出了

由於是PhantomReference,因此在垃圾回收前要將PhantomReference加入到其註冊的佇列中。


LinkedList<PhantomReference<VeryBig>> pa = new  LinkedList<PhantomReference<VeryBig>>();
for(int i=0;i<size;i  ){
pa.add(new PhantomReference(new VeryBig("Phantom " i),rq));
System.out.println("Just created: " pa.getLast());
checkQueue();
}

如果我們在上面程式碼後面也新增一個System.gc(),則也會對虛引用物件進行一個垃圾回收。

最後要說明的是:上面程式的執行結果每次執行可能都不會一致,這是因為當我們顯示呼叫System.gc();時JVM虛擬機器有時並不會立即執行。

例如:不立即執行的其中一種情況的結果為:

    Just created: Soft 0
Just created: Soft 1
Just created: Soft 2
Just created: Soft 3
Just created: Soft 4
Just created: Soft 5
Just created: Soft 6
Just created: Soft 7
Just created: Soft 8
Just created: Soft 9
Just created: Weak 0
Just created: Weak 1
Just created: Weak 2
Just created: Weak 3
Just created: Weak 4
Just created: Weak 5
Just created: Weak 6
Just created: Weak 7
Just created: Weak 8
Just created: Weak 9
Just created: [email protected]
Just created: [email protected]
Just created: [email protected]
Just created: [email protected]
Just created: [email protected]
Just created: [email protected]
Just created: [email protected]
Just created: [email protected]
Just created: [email protected]
Just created: [email protected]
Finalizing Weak
Finalizing Weak 9
Finalizing Weak 8
Finalizing Weak 7
Finalizing Weak 6
Finalizing Weak 5
Finalizing Weak 4
Finalizing Weak 3
Finalizing Weak 2
Finalizing Weak 1
Finalizing Weak 0

參考資料

1、http://hongjiang.info/java-referencequeue/

2、http://blog.sina.com.cn/s/blog_667ac0360102e9f3.html