欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解java中Reference的實(shí)現(xiàn)與相應(yīng)的執(zhí)行過(guò)程

 更新時(shí)間:2016年09月11日 08:45:17   作者:i flym  
不知道大家知不知道特殊的reference對(duì)象都是被jvm專門處理的,所以這篇文章就相應(yīng)的工作流程和referencequeue之間的協(xié)作進(jìn)行梳理.有需要的朋友們可以參考借鑒。

一、Reference類型(除強(qiáng)引用)

可以理解為Reference的直接子類都是由jvm定制化處理的,因此在代碼中直接繼承于Reference類型沒(méi)有任何作用.只能繼承于它的子類,相應(yīng)的子類類型包括以下幾種.(忽略沒(méi)有在java中使用的,如jnireference)

     SoftReference

     WeakReference

     FinalReference

     PhantomReference

上面的引用類型在相應(yīng)的javadoc中也有提及.FinalReference專門為finalize方法設(shè)計(jì),另外幾個(gè)也有特定的應(yīng)用場(chǎng)景.其中softReference用在內(nèi)存相關(guān)的緩存當(dāng)中,weakReference用在與回收相關(guān)的大多數(shù)場(chǎng)景.phantomReference用在與包裝對(duì)象回收回調(diào)場(chǎng)景當(dāng)中(比如資源泄漏檢測(cè)).

可以直接在ide中查看幾個(gè)類型的子類信息,即可了解在大多數(shù)框架中,都是通過(guò)繼承相應(yīng)的類型用在什么場(chǎng)景當(dāng)中,以便于我們實(shí)際進(jìn)行選型處理.

二、Reference構(gòu)造函數(shù)

其內(nèi)部提供2個(gè)構(gòu)造函數(shù),一個(gè)帶queue,一個(gè)不帶queue.其中queue的意義在于,我們可以在外部對(duì)這個(gè)queue進(jìn)行監(jiān)控.即如果有對(duì)象即將被回收,那么相應(yīng)的reference對(duì)象就會(huì)被放到這個(gè)queue里.我們拿到reference,就可以再作一些事務(wù).

而如果不帶的話,就只有不斷地輪訓(xùn)reference對(duì)象,通過(guò)判斷里面的get是否返回null(phantomReference對(duì)象不能這樣作,其get始終返回null,因此它只有帶queue的構(gòu)造函數(shù)).這兩種方法均有相應(yīng)的使用場(chǎng)景,取決于實(shí)際的應(yīng)用.如weakHashMap中就選擇去查詢queue的數(shù)據(jù),來(lái)判定是否有對(duì)象將被回收.而ThreadLocalMap,則采用判斷get()是否為null來(lái)作處理.

相應(yīng)的構(gòu)造函數(shù)如下所示:

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

這里面的NULL隊(duì)列,即可以理解為不需要對(duì)其隊(duì)列中的數(shù)據(jù)作任何處理的隊(duì)列.并且其內(nèi)部也不會(huì)存取任何數(shù)據(jù).

在上面的對(duì)象中,referent表示其引用的對(duì)象,即我們?cè)跇?gòu)造的時(shí)候,需要被包裝在其中的對(duì)象.對(duì)象即將被回收的定義即此對(duì)象除了被reference引用之外沒(méi)有其它引用了(并非確實(shí)沒(méi)有被引用,而是gcRoot可達(dá)性不可達(dá),以避免循環(huán)引用的問(wèn)題).

queue即是對(duì)象即被回收時(shí)所要通知的隊(duì)列,當(dāng)對(duì)象即被回收時(shí),整個(gè)reference對(duì)象(而不是被回收的對(duì)象)會(huì)被放到queue里面,然后外部程序即可通過(guò)監(jiān)控這個(gè)queue拿到相應(yīng)的數(shù)據(jù)了.

三、ReferenceQueue及Reference引用鏈

這里的queue名義上是一個(gè)隊(duì)列,但實(shí)際內(nèi)部并非有實(shí)際的存儲(chǔ)結(jié)構(gòu),它的存儲(chǔ)是依賴于內(nèi)部節(jié)點(diǎn)之間的關(guān)系來(lái)表達(dá).可以理解為queue是一個(gè)類似于鏈表的結(jié)構(gòu),這里的節(jié)點(diǎn)其實(shí)就是reference本身.可以理解為queue為一個(gè)鏈表的容器,其自己僅存儲(chǔ)當(dāng)前的head節(jié)點(diǎn),而后面的節(jié)點(diǎn)由每個(gè)reference節(jié)點(diǎn)自己通過(guò)next來(lái)保持即可.

Reference狀態(tài)值

每個(gè)引用對(duì)象都有相應(yīng)的狀態(tài)描述,即描述自己以及包裝的對(duì)象當(dāng)前處于一個(gè)什么樣的狀態(tài),以方便進(jìn)行查詢,定位或處理.

     1、Active:活動(dòng)狀態(tài),即相應(yīng)的對(duì)象為強(qiáng)引用狀態(tài),還沒(méi)有被回收,這個(gè)狀態(tài)下對(duì)象不會(huì)放到queue當(dāng)中.在這個(gè)狀態(tài)下next為null,queue為定義時(shí)所引用的queue.

     2、Pending:準(zhǔn)備放入queue當(dāng)中,在這個(gè)狀態(tài)下要處理的對(duì)象將挨個(gè)地排隊(duì)放到queue當(dāng)中.在這個(gè)時(shí)間窗口期,相應(yīng)的對(duì)象為pending狀態(tài).不管什么reference,進(jìn)入到此狀態(tài)的,即可認(rèn)為相應(yīng)的此狀態(tài)下,next為自己(由jvm設(shè)置),queue為定義時(shí)所引用的queue.

     3、Enqueued:相應(yīng)的對(duì)象已經(jīng)為待回收,并且相應(yīng)的引用對(duì)象已經(jīng)放到queue當(dāng)中了.準(zhǔn)備由外部線程來(lái)詢循queue獲取相應(yīng)的數(shù)據(jù).此狀態(tài)下,next為下一個(gè)要處理的對(duì)象,queue為特殊標(biāo)識(shí)對(duì)象ENQUEUED.

     4、Inactive:即此對(duì)象已經(jīng)由外部從queue中獲取到,并且已經(jīng)處理掉了.即意味著此引用對(duì)象可以被回收,并且對(duì)內(nèi)部封裝的對(duì)象也可以被回收掉了(實(shí)際的回收運(yùn)行取決于clear動(dòng)作是否被調(diào)用).可以理解為進(jìn)入到此狀態(tài)的肯定是應(yīng)該被回收掉的.

jvm并不需要定義狀態(tài)值來(lái)判斷相應(yīng)引用的狀態(tài)處于哪個(gè)狀態(tài),只需要通過(guò)計(jì)算next和queue即可進(jìn)行判斷.

四、ReferenceQueue#head

始終保存當(dāng)前隊(duì)列中最新要被處理的節(jié)點(diǎn),可以認(rèn)為queue為一個(gè)后進(jìn)先出的隊(duì)列.當(dāng)新的節(jié)點(diǎn)進(jìn)入時(shí),采取以下的邏輯

newE.next = head;head=newE;

然后,在獲取的時(shí)候,采取相應(yīng)的邏輯

tmp = head;head=tmp.next;return tmp;

五、Reference#next

即描述當(dāng)前引用節(jié)點(diǎn)所存儲(chǔ)的下一個(gè)即將被處理的節(jié)點(diǎn).但next僅在放到queue中才會(huì)有意義.為了描述相應(yīng)的狀態(tài)值,在放到隊(duì)列當(dāng)中后,其queue就不會(huì)再引用這個(gè)隊(duì)列了.而是引用一個(gè)特殊的ENQUEUED.因?yàn)橐呀?jīng)放到隊(duì)列當(dāng)中,并且不會(huì)再次放到隊(duì)列當(dāng)中.

六、Reference#referent

即描述當(dāng)前引用所引用的實(shí)際對(duì)象,正如在注解中所述,其會(huì)被仔細(xì)地處理.即此什么什么時(shí)候會(huì)被回收,如果一旦被回收,則會(huì)直接置為null,而外部程序可通過(guò)通過(guò)引用對(duì)象本身(而不是referent)了解到,回收行為的產(chǎn)生.

七、ReferenceQueue#enqueue 待處理引用入隊(duì)

此過(guò)程即在reference對(duì)象從active->pending->enqued的過(guò)程. 此方法為處理pending狀態(tài)的對(duì)象為enqued狀態(tài).相應(yīng)的過(guò)程即為之前的邏輯,即將一個(gè)節(jié)點(diǎn)入隊(duì)操作,相應(yīng)的代碼如下所示.

r.queue = ENQUEUED;
r.next = (head == null) ? r : head;
head = r;
queueLength++;
lock.notifyAll();

最后的nitify即通知外部程序之前阻塞在當(dāng)前隊(duì)列之上的情況.(即之前一直沒(méi)有拿到待處理的對(duì)象)

八、Reference#tryHandlePending

即處理reference對(duì)象從active到pending狀態(tài)的變化.在Reference對(duì)象內(nèi)部,有一個(gè)static字段,其相應(yīng)的聲明如下:

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

可以理解為jvm在gc時(shí)會(huì)將要處理的對(duì)象放到這個(gè)靜態(tài)字段上面.同時(shí),另一個(gè)字段discovered,表示要處理的對(duì)象的下一個(gè)對(duì)象.即可以理解要處理的對(duì)象也是一個(gè)鏈表,通過(guò)discovered進(jìn)行排隊(duì),這邊只需要不停地拿到pending,然后再通過(guò)discovered不斷地拿到下一個(gè)對(duì)象即可.因?yàn)檫@個(gè)pending對(duì)象,兩個(gè)線程都可能訪問(wèn),因此需要加鎖處理.

相應(yīng)的處理過(guò)程如下所示:

if (pending != null) {
 r = pending;
 // 'instanceof' might throw OutOfMemoryError sometimes
 // so do this before un-linking 'r' from the 'pending' chain...
 c = r instanceof Cleaner ? (Cleaner) r : null;
 // unlink 'r' from 'pending' chain
 pending = r.discovered;
 r.discovered = null;
} 
//將處理對(duì)象入隊(duì),即進(jìn)入到enqued狀態(tài)
ReferenceQueue<? super Object> q = r.queue;
if (q != ReferenceQueue.NULL) q.enqueue(r);

九、Reference#clear

清除引用對(duì)象所引用的原對(duì)象,這樣通過(guò)get()方法就不能再訪問(wèn)到原對(duì)象了.從相應(yīng)的設(shè)計(jì)思路來(lái)說(shuō),既然都進(jìn)入到queue對(duì)象里面,就表示相應(yīng)的對(duì)象需要被回收了,因?yàn)闆](méi)有再訪問(wèn)原對(duì)象的必要.此方法不會(huì)由JVM調(diào)用,而jvm是直接通過(guò)字段操作清除相應(yīng)的引用,其具體實(shí)現(xiàn)與當(dāng)前方法相一致.

clear的語(yǔ)義就是將referent置null.

     WeakReference對(duì)象進(jìn)入到queue之后,相應(yīng)的referent為null.

     SoftReference對(duì)象,如果對(duì)象在內(nèi)存足夠時(shí),不會(huì)進(jìn)入到queue,自然相應(yīng)的reference不會(huì)為null.如果需要被處理(內(nèi)存不夠或其它策略),則置相應(yīng)的referent為null,然后進(jìn)入到queue.

     FinalReference對(duì)象,因?yàn)樾枰{(diào)用其finalize對(duì)象,因此其reference即使入queue,其referent也不會(huì)為null,即不會(huì)clear掉.

     PhantomReference對(duì)象,因?yàn)楸旧韌et實(shí)現(xiàn)為返回null.因此clear的作用不是很大.因?yàn)椴还躤nqueue還是沒(méi)有,都不會(huì)清除掉.

十、ReferenceHandler enqueue線程

上面提到j(luò)vm會(huì)將要處理的對(duì)象設(shè)置到pending對(duì)象當(dāng)中,因此肯定有一個(gè)線程來(lái)進(jìn)行不斷的enqueue操作,此線程即引用處理器線程,其優(yōu)先級(jí)為MAX_PRIORITY,即最高.相應(yīng)的啟動(dòng)過(guò)程為靜態(tài)初始化創(chuàng)建,可以理解為當(dāng)任何使用到Reference對(duì)象或類時(shí),此線程即會(huì)被創(chuàng)建并啟動(dòng).相應(yīng)的代碼如下所示:

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();
}

其優(yōu)先級(jí)最高,可以理解為需要不斷地處理引用對(duì)象.在通過(guò)jstack打印運(yùn)行線程時(shí),相應(yīng)的Reference Handler即是指在這里初始化的線程,如下所示:

十一、JVM相關(guān)

在上述的各個(gè)處理點(diǎn)當(dāng)中,都與JVM的回收過(guò)程相關(guān).即認(rèn)為gc流程會(huì)與相應(yīng)的reference協(xié)同工作.如使用cms收集器,在上述的整個(gè)流程當(dāng)中,涉及到preclean過(guò)程,也涉及到softReference的重新標(biāo)記處理等,同時(shí)對(duì)reference對(duì)象的各種處理也需要與具體的類型相關(guān)進(jìn)行協(xié)作.相應(yīng)的JVM處理,采用C++代碼,因此需要好好地再理一下.

十二、總結(jié)

與finalReference對(duì)象相同,整個(gè)reference和referenceQueue都是一組協(xié)同工作的處理組,為保證不同的引用語(yǔ)義,通過(guò)與jvm gc相關(guān)的流程一起作用,最終實(shí)現(xiàn)不同場(chǎng)景,不同引用級(jí)別的處理.

另外,由于直接使用referenceQueue,再加上開(kāi)啟線程去監(jiān)控queue太過(guò)麻煩和復(fù)雜.可以參考由google guava實(shí)現(xiàn)的 FinalizableReferenceQueue 以及相應(yīng)的FinalizableReference對(duì)象.可以簡(jiǎn)化一點(diǎn)點(diǎn)處理過(guò)程.以上就是這篇文章的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)或者工作帶來(lái)一定的幫助。

相關(guān)文章

最新評(píng)論