Reference引用源碼解析

一、JAVA引用

JAVA中有四種引用,強(qiáng)引用、軟引用、弱引用、虛引用,而這些引用在代碼層次都繼承了Reference<T>這個類。

JAVA引用類關(guān)系圖

上面可以看到常見的虛引用(PhantomReference),弱引用(WeakReference),軟引用(SoftReference),至于Finalizer和Cleaner在后續(xù)文章中詳述。

二、Reference源碼

1.源碼位置

在JDK中會有src.zip,解壓src.zip會在java\lang\ref中查看到Reference.java。

JDK中源碼包

2.Reference中原理

引用是一種管理JAVA內(nèi)存的方式,如果對象存在強(qiáng)引用,GC的時候不會將對象回收,而如果對象有且僅有軟引用(內(nèi)存不足時),弱引用時,對象會被GC回收,而Reference則是管理軟引用,弱引用的基礎(chǔ)。

Reference就是為對象創(chuàng)建一個非強(qiáng)引用,當(dāng)對象沒有強(qiáng)引用存在時,JVM會對對象進(jìn)行標(biāo)識,并且在Reference中進(jìn)行處理。

I.Reference中的ReferenceQueue

Reference中包裹的對象當(dāng)沒有強(qiáng)引用存在時,即使是代碼中存在對它的(弱引用、軟引用、虛引用)時,對象也會被回收,對象被回收時有時候需要被監(jiān)聽,此時通過ReferenceQueue來處理,將Reference加入到ReferenceQueue中,通過對ReferenceQueue的處理來通過那些監(jiān)聽對象,如WeakHashMap中,Key是弱引用的,當(dāng)某個Key包裹的對象不存在強(qiáng)引用時,這個Key會被回收掉,當(dāng)時當(dāng)這個Key被回收掉時,Map并未得到通知,此時需要將Key被回收掉通知給Map以便Map刪除這個Key,此時就需要將Key這個引用對象加入到Queue中,以便處理Queue從中獲知Key已經(jīng)被回收了,從而處理Map刪除Key。

II.Reference對象的狀態(tài)

1.Active 對象在創(chuàng)建時初始的狀態(tài)

2.Pending 如果存在queue,此狀態(tài)會出現(xiàn),表示對象準(zhǔn)備被加入到queue中

3.Enqueued 如果存在queue,此狀態(tài)會出現(xiàn),表示對象已經(jīng)加入到了queue

4.Inactive 引用對象失效,此狀態(tài)未終結(jié)狀態(tài)

狀態(tài)轉(zhuǎn)換圖

II.Reference對象狀態(tài)的標(biāo)記

Reference中沒有定義專門的變量來標(biāo)記當(dāng)前的狀態(tài),而是通過queue和next的值共同作用來標(biāo)記當(dāng)前的狀態(tài)。

Active:如果創(chuàng)建Reference對象時,沒有傳入ReferenceQueue,queue = NULL。如果有傳入,則queue指向傳入的ReferenceQueue隊(duì)列對象,next==null。

Pending:queue為初始化時傳入ReferenceQueue對象;next==this;

Enqueue:queue==ReferenceQueue.ENQUEUED;next為queue中下一個reference對象,或者若為最后一個了next==this;

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

III.狀態(tài)變更操作(即上述值得改變)

由于沒有讀JVM源碼,所以下面很多由JVM引起的改變只是猜測,望懂得大神給予指點(diǎn)。

A.Active---->Inactive,此過程在JVM感知某個對象不存在強(qiáng)引用的情況下,將next指針指向自己,以達(dá)到?jīng)]有讓引用對象失效的狀態(tài)。

B. Active---->Pending,此同上,將next指針指向自己。

C. Pending----->Enqueue,此過程由Reference中的tryHandlePending調(diào)用ReferenceQueue中的enqueue處理,進(jìn)而將queue的取值賦值為ENQUEUED,將next賦值為自己(如果自己是queue中最后一個元素)或者是復(fù)制給queue隊(duì)列的head。

D.Pending---->?Inactive,這個就是使用者自己操作了,什么時候去處理queue中的對象,調(diào)用ReferenceQueue中的remove,queue賦值為NULL,next指向自己。

IV.discovered變量和pending變量。

這兩個變量為JVM與Reference對象交互的關(guān)鍵,由JVM改變二者的值。

pending 是一個靜態(tài)變量,也就是所有Reference對象共享的,表示準(zhǔn)備加入到queue的隊(duì)列,而作為一個隊(duì)列需要有next指針將隊(duì)列串聯(lián),而discovered在某種情況下就承擔(dān)了這個角色。

下面是個人的一些猜想:

關(guān)于引用,維護(hù)了兩個隊(duì)列,一個是discovered隊(duì)列,一個是pending隊(duì)列,前者表示對象可以被回收,?JVM經(jīng)過一些處理后,將對象加入到pending隊(duì)列,等待加入到queue。

此處discovered在不同的狀態(tài)下有兩種含義,當(dāng)對象處于active狀態(tài)時,discovered指向的是可以被回收的對象,當(dāng)對象處于pending狀態(tài)時,discovered指向的是pending隊(duì)列的下一個元素。

disvoered的雙重含義

JVM中判定對象被回收,則先加入到Discovered隊(duì)列中,經(jīng)過一些數(shù)據(jù)操作,將對象加入到Pending隊(duì)列中,舉例:上述中D4經(jīng)過處理,添加到pending隊(duì)列中,則P4的discovered=D4,

D4.discovered=NULL,而后D3加入,D4.discovered=D3.以此類推。

而pending隊(duì)列的出隊(duì)操作,在tryHandlePending中操作。下面將對象出列,先將pending指向下一個對象即localReference.discovered;,而后將彈出的對象的discovered=null。

pending的出隊(duì)操作

三、疑問

I.被包裹的對象何時被GC銷毀的?

II.Reference本身也是內(nèi)存對象,何時被銷毀?

III.Discovered列表何時被創(chuàng)建?

四、鏈接

GC中的Discovered隊(duì)列:http://www.importnew.com/21628.html
很全面的講解了Reference的代碼:https://cloud.tencent.com/developer/article/1366147

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容