一、JAVA引用
JAVA中有四種引用,強(qiáng)引用、軟引用、弱引用、虛引用,而這些引用在代碼層次都繼承了Reference<T>這個類。
上面可以看到常見的虛引用(PhantomReference),弱引用(WeakReference),軟引用(SoftReference),至于Finalizer和Cleaner在后續(xù)文章中詳述。
二、Reference源碼
1.源碼位置
在JDK中會有src.zip,解壓src.zip會在java\lang\ref中查看到Reference.java。
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)
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ì)列的下一個元素。
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。
三、疑問
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