首頁  >  文章  >  Java  >  詳解Java Reference源碼分析程式碼

詳解Java Reference源碼分析程式碼

高洛峰
高洛峰原創
2017-03-19 15:37:561816瀏覽

@(Java)[Reference]

Java Reference 原始碼分析

  Reference物件封裝了其它物件的引用,可以和普通的物件一樣操作,在在一定的限制條件下,支援和垃圾收集器的交互作用。即可以使用Reference對象來引用其它對象,但最後還是會被垃圾收集器回收。程序有時也需要在物件回收後被通知,以告知對象的可及性發生變更。
  Java提供了四種不同類型的引用,引用等級從高到低分別為FinalReference,SoftReference,WeakReference,PhantomReference。其中FinalReference並未對外提供使用。每種類型對應著不同程度的可及性。

簡介

強引用FinalReference

#強引用指的是,程式中有直接可達的引用,而不需要透過任何引用對象,如Object obj = new Object();中,obj為強引用。

軟引用SoftReference

軟引用,非強引用,但可以透過軟引用物件來存取。軟引用的對象,只有在記憶體不足的時候(拋出OOM異常前),垃圾收集器會決定回收該軟引用所指向的對象。軟引用通常用於實現記憶體敏感的快取。

SoftReference<Object> softRef = new SoftReference<Object>(new Object());

弱引用WeakReference

弱引用,非強引用和軟引用,但可以透過弱引用物件來存取。弱引用的對象,不管記憶體是否足夠,只要被垃圾收集器發現,該引用的對象就會被回收。實際的應用請見WeakHashMap等。

WeakReference<Object> weakRef = new WeakReference<Object>(new Object());

虛引用PhantomReference

虛引用,該引用必須和引用隊列(ReferenceQueue)一起使用,一般用於實現追蹤垃圾收集器的回收動作,例如在物件被回收的時候,會呼叫該物件的finalize方法,在使用虛引用可以實現該動作,也更加安全。

Object obj = new Object();
ReferenceQueue<Object> refQueue = new ReferenceQueue<>();
PhantomReference<Object> phantom = new PhantomReference<Object>(obj, refQueue);

ReferenceQueue

該隊列作為引用中的一員,可以和上述三種引用類型組合使用,該隊列的作用是:建立Reference時,將Queue註冊到Reference中,當該Reference所引用的物件被垃圾收集器回收時,會將該Reference放到該佇列中,相當於一種通知機制。
範例Demo1:

ReferenceQueue queue = new ReferenceQueue();
WeakReference reference = new WeakReference(new Object(), queue);
System.out.println(reference);
System.gc();
Reference reference1 = queue.remove();
System.out.println(reference1);

原始碼分析

Reference和ReferenceQueue

Reference內部有幾個比較重要的屬性

// 用于保存对象的引用,GC会根据不同Reference来特别对待
private T referent;
// 如果需要通知机制,则保存的对对应的队列
ReferenceQueue<? super T> queue;
/* 这个用于实现一个单向循环链表,用以将保存需要由ReferenceHandler处理的引用 */
Reference next;
static private class Lock { };
// 锁,用于同步pending队列的进队和出队
private static Lock lock = new Lock();
// 此属性保存一个PENDING的队列,配合上述next一起使用
private static Reference pending = null;

狀態

详解Java Reference源码分析代码

內部類別ReferenceHandler

#ReferenceHandler作為Reference的靜態內部類,用於實作將pending佇列裡面的Reference實例依序加入不同的ReferenceQueue中(取決於Reference裡面的queue)。此pending的元素由GC負責加入。
註:這裡對pending佇列進行加鎖,個人認為是因為GC執行緒可能和ReferenceHandler所在的執行緒並發執行,如GC採用CMS並發收集的時候。
如下程式碼所示

// 此线程在静态块中启动,即一旦使用了Reference,则会启动该线程
private static class ReferenceHandler extends Thread {
    public void run() {
        for (;;) {

            Reference r;
            synchronized (lock) {
                if (pending != null) {
                    r = pending;
                    Reference rn = r.next;
                    // 从pending中取下一个元素,如果后继为空,则next指向自身                    pending = (rn == r) ? null : rn;
                    r.next = r;
                } else {
                    try {
                        // 没有则等待,后续加入元素会调用lock.notify唤醒
                        lock.wait();
                    } catch (InterruptedException x) { }
                    continue;
                }
            }
            // ...
            ReferenceQueue q = r.queue;
            // 如果该Reference注册了对应的Queue,则加入到该Queue中
            if (q != ReferenceQueue.NULL) q.enqueue(r);
        }
    }
}

ReferenceQueue屬性

// 用于标识没有注册Queue
static ReferenceQueue NULL = new Null();
// 用于标识已经处于对应的Queue中
static ReferenceQueue ENQUEUED = new Null();

static private class Lock { };
/* 互斥锁,用于同步ReferenceHandler的enqueue和用户线程操作的remove和poll出队操作 */
private Lock lock = new Lock();
// 队列
private volatile Reference<? extends T> head = null;
// 队列中的元素个数
private long queueLength = 0;

ReferenceQueue.enqueue

只會透過Reference裡要呼叫該方法,用於將Reference放入到目前佇列中

boolean enqueue(Reference<? extends T> r) {    synchronized (r) {        // 判断是否已经入队了
        if (r.queue == ENQUEUED) return false;        synchronized (lock) {
            r.queue = ENQUEUED;            // 单向循环
            r.next = (head == null) ? r : head;
            head = r;
            queueLength++;            if (r instanceof FinalReference) {
                sun.misc.VM.addFinalRefCount(1);
            }            // 通知当前挂起的线程(调用remove时有可能会挂起)
            lock.notifyAll();            return true;
        }
    }
}

ReferenceQueue.remove

public Reference<? extends T> remove(long timeout)    throws IllegalArgumentException, InterruptedException
{    if (timeout < 0) {        throw new IllegalArgumentException("Negative timeout value");
    }    synchronized (lock) {        // 从队列中取出一个元素
        Reference<? extends T> r = reallyPoll();        // 如果不为空,则直接返回
        if (r != null) return r;        for (;;) {            // 否则等待,由enqueue时notify唤醒
            lock.wait(timeout);
            r = reallyPoll();            if (r != null) return r;            if (timeout != 0) return null;
        }
    }
}

具體執行流程

以上述範例Demo1作為分析

// 创建一个引用队列
ReferenceQueue queue = new ReferenceQueue();

// 创建虚引用,此时状态为Active,并且Reference.pending为空,当前Reference.queue = 上面创建的queue,并且next=null
WeakReference reference = new WeakReference(new Object(), queue);
System.out.println(reference);
// 当GC执行后,由于是虚引用,所以回收该object对象,并且置于pending上,此时reference的状态为PENDING
System.gc();

/* ReferenceHandler从pending中取下该元素,并且将该元素放入到queue中,此时Reference状态为ENQUEUED,Reference.queue = ReferenceENQUEUED */

/* 当从queue里面取出该元素,则变为INACTIVE,Reference.queue = Reference.NULL */
Reference reference1 = queue.remove();
System.out.println(reference1);

以上是詳解Java Reference源碼分析程式碼的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn