淺析Java Reference__java

來源:互聯網
上載者:User

  Java1.2引入了新的概念——Reference,在這之前都是預設的強引用,即Strong Reference。在GC過程中,只要從GC Roots通過強引用有路徑可達則說明接下來的程式還可能用到,就不能回收,反之則回收。
  但是這種方式比較死板,為了增加記憶體回收的靈活性便有了java.lang.ref類庫,裡頭包含最重要的抽象類別Reference,及其三個繼承類:SoftReference(軟引用)、WeakReference(弱引用)和PhantomReference(幻影引用)。當記憶體回收行程正在考察的對象只能通過上述三個中某個Reference對象才可獲得時,這三個Reference衍生類別會為GC提供不同的指示: 當JVM報告記憶體不足的時候,所有只被SoftReference所指向的對象會被GC回收,否則不會回收; 當GC發現一個對象只能通過弱引用對象可達,將會釋放WeakReference所引用的對象。 當GC發現一個對象只能通過幻影引用對象可達,將會將PhantomReference對象插入與其關聯的ReferenceQueue隊列中(PhantomReference對象的初始化必須有一個ReferenceQueue隊列)。而此時PhantomReference所指向的對象只是執行了finalize方法,但是對象本身並沒有被GC回收,要等到ReferenceQueue被真正的處理(如調用remove方法)後才會被回收。

那麼上述功能是如何完成的呢。下面藉助JDK源碼簡單分析下:
  在Reference類中有個域referent用來儲存所指對象的引用:

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

在初始化Reference對象時,referent會指向傳遞進來的對象。在GC時如果發現目標對象只有Reference對象中的referent指向時,就會根據上述三條規則執行回收策略。我們看到第二個建構函式另外包括一個ReferenceQueue對象,我們很多後續操作都需要依靠這個對象完成,那這個對象是幹嘛用的呢。
  GC回收Reference所指對象後(這裡實際指referent所指向的對象,另外PhantomReference是在回收前,finalize後),將Reference添加到與其綁定的ReferenceQueue中,程式通過調用ReferenceQueue的remove方法或poll方法來感知reference被GC回收的事件。下面是一段Reference的源碼:

static private class Lock { };private static Lock lock = new Lock();/* 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.*/ private static Reference pending = null; /* High-priority thread to enqueue pending References  */ private static class ReferenceHandler extends Thread {     ReferenceHandler(ThreadGroup g, String name) {         super(g, name);     }     public void run() {         for (;;) {             Reference r;             synchronized (lock) {                 if (pending != null) {                     r = pending;                     Reference rn = r.next;                     pending = (rn == r) ? null : rn;                     r.next = r;                 } else {                     try {                         lock.wait();                     } catch (InterruptedException x) { }                     continue;                 }             }             // Fast path for cleaners             if (r instanceof Cleaner) {                 ((Cleaner)r).clean();                 continue;             }             ReferenceQueue 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(); }

ReferenceHandler這個線程在Reference類初始化的時候就作為守護進程啟動。注意這裡有一個Reference類型的成員變數pending,ReferenceHandler會迴圈檢測pending是否為空白,如果為空白就掛起等待。當Reference所指向的對象在GC階段被回收,就會被添加到pending上,然後喚醒ReferenceHandler,將Reference加入ReferenceQueue中。現在就可以利用ReferenceQueue做一些後續操作了,就拿經典的WeakHashMap來說一說,先看看它的Entry:

 private final ReferenceQueue<Object> queue = new ReferenceQueue<>();/** * The entries in this hash table extend WeakReference, using its main ref * field as the key. */private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {    V value;    int hash;    Entry<K,V> next;    Entry(Object key, V value,          ReferenceQueue<Object> queue,          int hash, Entry<K,V> next) {        super(key, queue);  //用key來初始化referent        this.value = value;        this.hash  = hash;        this.next  = next;    }    //省略}

Entry繼承了WeakReference,所以它有了弱引用的性質。這裡讓弱引用指向key值所指的對象,所以當沒有其他更強的引用指向key所指對象時,目標對象會被GC掉,並將持有這個key的Entry加入queue所指的ReferenceQueue中去。既然key值都冇得了,自然這個Entry就沒有存在的意義了,所以得找個機會把它哢嚓掉。WeakHashMap把這個時間點設定到了每一次調用getTable或size方法時,首先進行一次清掃工作,而這個清掃工作正是依賴於引用隊列queue中記錄的引用對象。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.