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

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

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

一、Reference類型(除強引用)

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

     SoftReference

     WeakReference

     FinalReference

     PhantomReference

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

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

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

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

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

相應(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隊列,即可以理解為不需要對其隊列中的數(shù)據(jù)作任何處理的隊列.并且其內(nèi)部也不會存取任何數(shù)據(jù).

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

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

三、ReferenceQueue及Reference引用鏈

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

Reference狀態(tài)值

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

     1、Active:活動狀態(tài),即相應(yīng)的對象為強引用狀態(tài),還沒有被回收,這個狀態(tài)下對象不會放到queue當中.在這個狀態(tài)下next為null,queue為定義時所引用的queue.

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

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

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

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

四、ReferenceQueue#head

始終保存當前隊列中最新要被處理的節(jié)點,可以認為queue為一個后進先出的隊列.當新的節(jié)點進入時,采取以下的邏輯

newE.next = head;head=newE;

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

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

五、Reference#next

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

六、Reference#referent

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

七、ReferenceQueue#enqueue 待處理引用入隊

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

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

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

八、Reference#tryHandlePending

即處理reference對象從active到pending狀態(tài)的變化.在Reference對象內(nèi)部,有一個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時會將要處理的對象放到這個靜態(tài)字段上面.同時,另一個字段discovered,表示要處理的對象的下一個對象.即可以理解要處理的對象也是一個鏈表,通過discovered進行排隊,這邊只需要不停地拿到pending,然后再通過discovered不斷地拿到下一個對象即可.因為這個pending對象,兩個線程都可能訪問,因此需要加鎖處理.

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

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;
} 
//將處理對象入隊,即進入到enqued狀態(tài)
ReferenceQueue<? super Object> q = r.queue;
if (q != ReferenceQueue.NULL) q.enqueue(r);

九、Reference#clear

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

clear的語義就是將referent置null.

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

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

     FinalReference對象,因為需要調(diào)用其finalize對象,因此其reference即使入queue,其referent也不會為null,即不會clear掉.

     PhantomReference對象,因為本身get實現(xiàn)為返回null.因此clear的作用不是很大.因為不管enqueue還是沒有,都不會清除掉.

十、ReferenceHandler enqueue線程

上面提到j(luò)vm會將要處理的對象設(shè)置到pending對象當中,因此肯定有一個線程來進行不斷的enqueue操作,此線程即引用處理器線程,其優(yōu)先級為MAX_PRIORITY,即最高.相應(yīng)的啟動過程為靜態(tài)初始化創(chuàng)建,可以理解為當任何使用到Reference對象或類時,此線程即會被創(chuà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)先級最高,可以理解為需要不斷地處理引用對象.在通過jstack打印運行線程時,相應(yīng)的Reference Handler即是指在這里初始化的線程,如下所示:

十一、JVM相關(guān)

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

十二、總結(jié)

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

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

相關(guān)文章

最新評論