java的引用相關(guān)知識(shí)

關(guān)于java的引用類型以及對(duì)對(duì)象的回收涉及到以下幾個(gè)類,Reference、Reference子類(SoftReference、WeakReference、PhantomReference)以及ReferenceQueue,這篇文章就重點(diǎn)講解以下。

一. java對(duì)象的引用類型:

  • 1. 強(qiáng)引用(FinalReference)

強(qiáng)引用就是我們經(jīng)常使用的引用,其寫法如下:

StringBuffer buffer = new StringBuffer();

面創(chuàng)建了一個(gè)StringBuffer對(duì)象,并將這個(gè)對(duì)象的(強(qiáng))引用存到變量buffer中。如果一個(gè)對(duì)象具有強(qiáng)引用,那垃圾回收器絕不會(huì)回收它。當(dāng)內(nèi)存空間不足,Java虛擬機(jī)寧愿拋出OutOfMemoryError錯(cuò)誤,使程序異常終止,也不會(huì)靠隨意回收具有強(qiáng)引用的對(duì)象來(lái)解決內(nèi)存不足的問(wèn)題。

  • 2. 軟引用(SoftReference)

如果一個(gè)對(duì)象只具有軟引用,則內(nèi)存空間足夠,垃圾回收器就不會(huì)回收它;如果內(nèi)存空間不足了,就會(huì)回收這些對(duì)象的內(nèi)存。如果全部釋放完這些對(duì)象之后,內(nèi)存還不足,才會(huì)拋出OutOfMemory錯(cuò)誤。

由于軟引用可到達(dá)的對(duì)象比弱引用可達(dá)到的對(duì)象滯留內(nèi)存時(shí)間會(huì)長(zhǎng)一些,我們可以利用這個(gè)特性來(lái)做緩存。

SoftReference<Widget> softWidget = new SoftReference<Widget>(widget);

軟引用可以和一個(gè)引用隊(duì)列(ReferenceQueue)聯(lián)合使用,如果軟引用所引用的對(duì)象被垃圾回收器回收,Java虛擬機(jī)就會(huì)把這個(gè)軟引用加入到與之關(guān)聯(lián)的引用隊(duì)列中,關(guān)于引用隊(duì)列我們?cè)诮酉聛?lái)講

  • 3. 弱引用(WeakReference)

弱引用簡(jiǎn)單來(lái)說(shuō)就是將對(duì)象留在內(nèi)存的能力不是那么強(qiáng)的引用。使用WeakReference,垃圾回收器會(huì)幫你來(lái)決定引用的對(duì)象何時(shí)回收并且將對(duì)象從內(nèi)存移除。創(chuàng)建弱引用如下:


WeakReference<Widget> weakWidget = new WeakReference<Widget>(widget);

使用weakWidget.get()就可以得到真實(shí)的Widget對(duì)象,因?yàn)槿跻貌荒茏钃趵厥掌鲗?duì)其回收,你會(huì)發(fā)現(xiàn)(當(dāng)沒有任何強(qiáng)引用到widget對(duì)象時(shí))使用get時(shí)突然返回null。

弱引用可以和一個(gè)引用隊(duì)列(ReferenceQueue)聯(lián)合使用,如果弱引用所引用的對(duì)象被垃圾回收,Java虛擬機(jī)就會(huì)把這個(gè)弱引用加入到與之關(guān)聯(lián)的引用隊(duì)列中。

  • 其中一個(gè)應(yīng)用就是WeakHashMap,WeakHashMap和HashMap幾乎一樣,唯一的區(qū)別就是它的鍵(不是值!!!)使用WeakReference引用。當(dāng)WeakHashMap的鍵標(biāo)記為垃圾的時(shí)候,這個(gè)鍵對(duì)應(yīng)的條目就會(huì)自動(dòng)被移除。這就避免了上面不需要的Widget對(duì)象手動(dòng)刪除的問(wèn)題。這里是和ReferenceQueue聯(lián)合使用

  • 另外一個(gè)應(yīng)用就是ThreadLocal中也應(yīng)用到了WeakReference。ThreadLocal中并沒有將ReferenceQueue聯(lián)合使用

  • 4. 虛引用(PhantomReference)

在Object類里面有個(gè)finalize方法,其設(shè)計(jì)的初衷是在一個(gè)對(duì)象被真正回收之前,可以用來(lái)執(zhí)行一些清理的工作。但是問(wèn)題在于垃圾回收器的運(yùn)行時(shí)間是不固定的,所以這些清理工作的實(shí)際運(yùn)行時(shí)間也是不能預(yù)知的。虛引用(phantom reference)可以解決這個(gè)問(wèn)題。在創(chuàng)建幽靈引用PhantomReference的時(shí)候必須要指定一個(gè)引用隊(duì)列。當(dāng)一個(gè)對(duì)象的finalize方法已經(jīng)被調(diào)用了之后,這個(gè)對(duì)象的幽靈引用會(huì)被加入到隊(duì)列中。通過(guò)檢查該隊(duì)列里面的內(nèi)容就知道一個(gè)對(duì)象是不是已經(jīng)準(zhǔn)備要被回收了。

虛引用指向的對(duì)象十分脆弱,我們不可以通過(guò)get方法來(lái)得到其指向的對(duì)象。它的唯一作用就是當(dāng)其指向的對(duì)象被回收之后,自己被加入到引用隊(duì)列,用作記錄該引用指向的對(duì)象已被銷毀。

虛引用與軟引用和弱引用的一個(gè)區(qū)別在于:虛引用必須和引用隊(duì)列 (ReferenceQueue)聯(lián)合使用。當(dāng)垃圾回收器準(zhǔn)備回收一個(gè)對(duì)象時(shí),如果發(fā)現(xiàn)它還有虛引用,就會(huì)在回收對(duì)象的內(nèi)存之前,把這個(gè)虛引用加入到與之 關(guān)聯(lián)的引用隊(duì)列中。

二. 引用隊(duì)列(ReferenceQueue)

上面的這些概念中都提到了ReferenceQueue,下面我們就來(lái)看看ReferenceQueue是什么.
首先看源碼中的介紹

Reference queues, to which registered reference objects are appended by the garbage collector after the appropriate reachability changes are detected.
引用隊(duì)列,在檢測(cè)到適當(dāng)?shù)目傻竭_(dá)性更改后,垃圾回收器將已注冊(cè)的引用對(duì)象添加到該隊(duì)列中

查看源代碼會(huì)發(fā)現(xiàn)它很簡(jiǎn)單,實(shí)現(xiàn)了一個(gè)隊(duì)列的入隊(duì)(enqueue)和出隊(duì)(poll還有remove)操作,內(nèi)部元素就是泛型的Reference。
那么這個(gè)ReferenceQueue是如何使用的呢?我們接著看與它關(guān)系緊密的Reference。

引用(Reference)

同樣先看源碼中對(duì)這個(gè)類的介紹:

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.

這里可以看到,這個(gè)類是和垃圾回收緊密相關(guān)的,上面介紹的四個(gè)子類(四種引用類型)垃圾回收的方式不同。我們來(lái)具體看看Reference的實(shí)現(xiàn)。
Reference實(shí)例有四種狀態(tài)(建議直接看源碼,翻譯的話總會(huì)感覺少了點(diǎn)東西)

 *     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.
 *
 *     Pending: An element of the pending-Reference list, waiting to be
 *     enqueued by the Reference-handler thread.  Unregistered instances
 *     are never in this state.
 *
 *     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.
 *
 *     Inactive: Nothing more to do.  Once an instance becomes Inactive its
 *     state will never change again.

從數(shù)據(jù)結(jié)構(gòu)上看,Reference類內(nèi)部主要的成員有

     private T referent;         /* Treated specially by GC */

     volatile ReferenceQueue<? super T> queue;
     /* When active:   NULL
      *     pending:   this
      *    Enqueued:   next reference in queue (or this if last)
      *    Inactive:   this
      */
     @SuppressWarnings("rawtypes")
     Reference next;

     /* 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 */

      /* 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;

這個(gè)queue是通過(guò)構(gòu)造函數(shù)傳入的,表示創(chuàng)建一個(gè)Reference時(shí),要將其注冊(cè)到那個(gè)queue上。

    Reference(T referent) {
        this(referent, null);
    }

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

Queue的另一個(gè)作用是可以區(qū)分不同狀態(tài)的Reference。Reference有4種狀態(tài),不同狀態(tài)的reference其queue和next的規(guī)則如下不同:

* The state is encoded in the queue and next fields as follows:
*
*     Active: queue = ReferenceQueue with which instance is registered, or
*     ReferenceQueue.NULL if it was not registered with a queue; next =
*     null.
*
*     Pending: queue = ReferenceQueue with which instance is registered;
*     next = this
*
*     Enqueued: queue = ReferenceQueue.ENQUEUED; next = Following instance
*     in queue, or this if at end of list.
*
*     Inactive: queue = ReferenceQueue.NULL; next = this.
*

說(shuō)說(shuō)自己的理解:就是Reference有四種狀態(tài),每一種狀態(tài)都都相應(yīng)的queue和next變量,建議看源碼部分的對(duì)應(yīng)關(guān)系時(shí)要仔細(xì)理解。

ReferenceHandler線程

這個(gè)線程在Reference類的static構(gòu)造塊中啟動(dòng),并且被設(shè)置為高優(yōu)先級(jí)和daemon狀態(tài)。此線程要做的事情,是不斷的檢查pending 是否為null,如果pending不為null,則將pending進(jìn)行enqueue,否則線程進(jìn)入wait狀態(tài)。

ReferenceHandler線程要做的是將pending對(duì)象enqueue,但默認(rèn)我們所提供的queue,也就是從構(gòu)造函數(shù)傳入的是null,實(shí)際是使用了ReferenceQueue.NULL,Handler線程判斷queue為ReferenceQueue.NULL則不進(jìn)行操作,只有非ReferenceQueue.NULL的queue才會(huì)將Reference進(jìn)行enqueue。

ReferenceQueue.NULL相當(dāng)于我們提供了一個(gè)空的Queue去監(jiān)聽垃圾回收器給我們的反饋,并且對(duì)這種反饋不做任何處理。要處理反饋,則必須要提供一個(gè)非ReferenceQueue.NULL的queue。

在WeakHashMap則在內(nèi)部提供了一個(gè)非NULL的ReferenceQueue

private final ReferenceQueue<K> queue = new ReferenceQueue<K>();

在 WeakHashMap 添加一個(gè)元素時(shí),會(huì)使用 此queue來(lái)做監(jiān)聽器。見put方法中的下面一句:

tab[i] = new Entry<K,V>(k, value, queue, h, e);

這里Entry是一個(gè)內(nèi)部類,繼承了WeakReference

class Entry<K,V> extends WeakReference<K> implements Map.Entry<K,V>

WeakHashMap的 put, size, clear 都會(huì)間接或直接的調(diào)用到 expungeStaleEntries()方法。

expungeStaleEntries顧名思義,此方法的作用就是將 queue中陳舊的Reference進(jìn)行刪除,因?yàn)槠鋬?nèi)部的referent都已經(jīng)不可達(dá)了。所以也將這個(gè)WeakReference包裝的key從map中刪除。

參考鏈接

1.并發(fā)編程 | ThreadLocal源碼深入分析
2.深入分析 ThreadLocal 內(nèi)存泄漏問(wèn)題

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容