FinalReference引用
此類是一個(gè)package類型,表示它并不是公開(kāi)的一部分,繼承自Reference, 即表示也是一種特定的引用類型,因此每個(gè)包裝在其中的對(duì)象在被回收之前,自己都會(huì)放到指定的referqyebceQueue當(dāng)中.
這個(gè)引用對(duì)象專門為帶finalize方法的類服務(wù),可以理解為每一個(gè)有相應(yīng)的方法的對(duì)象,其都會(huì)封裝為一種finalRefernece對(duì)象.
因?yàn)閒inalize方法是object定義的,其默認(rèn)實(shí)現(xiàn)為空.那么如果重寫(xiě)了此方法,那么方法體肯定不為空.即可以通過(guò)這一種區(qū)別來(lái).只要finalize方法實(shí)現(xiàn)不為空的類,此產(chǎn)生的對(duì)象都需要被注冊(cè)到finalRefernece中.
這一步可以通過(guò)在newInstance的時(shí)候,即調(diào)用object默認(rèn)構(gòu)造方法的時(shí)候,就可以進(jìn)行相應(yīng)的注冊(cè)了.
Finalizer#register方法
主要調(diào)用了此方法,就會(huì)產(chǎn)生相應(yīng)的finalizer對(duì)象,而finalizer對(duì)象是繼承于finalReference的.此方法聲明如下:
1
2
3
4
|
/* Invoked by VM */ static void register(Object finalizee) { new Finalizer(finalizee); } |
從上面注釋可以看出,此方法會(huì)被jvm在特定時(shí)期調(diào)用.
然后切換到Finalizer的構(gòu)造方法,如下所示:
1
2
3
4
|
private Finalizer(Object finalizee) { super (finalizee, queue); add(); } |
可以看出,相應(yīng)的引用對(duì)象會(huì)通過(guò)queue進(jìn)行回調(diào).add的作用在于將所有還未進(jìn)行finalize方法的對(duì)象存起來(lái),在最后System.shutdown
時(shí)調(diào)用.通過(guò)Runtime#runFinalizersOnExit
進(jìn)行設(shè)置.
ReferenceQueue
此引用隊(duì)列會(huì)在相應(yīng)reference對(duì)象的內(nèi)部對(duì)象被回收之前放到此隊(duì)列中(詳細(xì)說(shuō)明在另一篇關(guān)于reference中再說(shuō)明.),因?yàn)橹恍枰獜拇岁?duì)列中拿到相應(yīng)的對(duì)象,那么此對(duì)象就肯定是準(zhǔn)備被回收的.
那么在回收之前調(diào)用相應(yīng)的finalize方法即可.
FinalizerThread線程
此線程即是從queue里面,不停的獲取數(shù)據(jù),然后調(diào)用相應(yīng)的finalize方法.相應(yīng)的代碼如下所示:
1
2
3
4
5
6
7
8
|
for (;;) { try { Finalizer f = (Finalizer)queue.remove(); f.runFinalizer(jla); } catch (InterruptedException x) { // ignore and continue } } |
而相應(yīng)的runFinalizer如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
synchronized ( this ) { if (hasBeenFinalized()) return ; remove(); } try { Object finalizee = this .get(); if (finalizee != null && !(finalizee instanceof java.lang.Enum)) { jla.invokeFinalize(finalizee); /* Clear stack slot containing this variable, to decrease the chances of false retention with a conservative GC */ finalizee = null ; } } catch (Throwable x) { } super .clear(); |
在上面的邏輯當(dāng)中,首先調(diào)用remove將其從未finalize中移除.這個(gè)方法是保證每個(gè)對(duì)象的finalize最多只會(huì)被調(diào)用一次,即當(dāng)前這次調(diào)用完了.它就會(huì)被記相應(yīng)的狀態(tài),即hasBeenFinalized返回為true(其實(shí)就是把里面的next指針指向自己.即自己從未finalize中移除,同時(shí)也不需要再次調(diào)用finalize了).
接下來(lái)就是調(diào)用相應(yīng)的finalize方法,上面的jla.invokeFinalize
其實(shí)就是調(diào)用相應(yīng)對(duì)象的finalize方法. 在這個(gè)處理中,首先通過(guò)get獲取原始對(duì)象.在整個(gè)jvm處理中,針對(duì)finalizeReference在回收之前默認(rèn)是不將引用設(shè)置為null.因?yàn)檫@里,總是能夠獲取相應(yīng)的引用對(duì)象.
處理完之后,最后調(diào)用相應(yīng)的clear,清除相應(yīng)的引用.這樣達(dá)到最終引用沒(méi)有其它對(duì)象可引用的效果.
在上面的處理當(dāng)中,并沒(méi)有限定調(diào)用finalize的時(shí)間.因此,一旦如果某個(gè)對(duì)象的finalize調(diào)用慢,就會(huì)影響到整個(gè)回收鏈的執(zhí)行,這下就會(huì)產(chǎn)生相應(yīng)的OOM異常了.因此,除非特殊情況,就不要重寫(xiě)finalize,相應(yīng)的場(chǎng)景都應(yīng)該有其它方法可以處理.比如guava中的FinalizableReference.
finalizer啟動(dòng)線程
在上面的線程,在相應(yīng)的進(jìn)程啟動(dòng)過(guò)程中就會(huì)被啟動(dòng).可以理解為,對(duì)象通過(guò)調(diào)用register(object)
觸發(fā)finalizer類的初始化.然后,在靜態(tài)初始化塊當(dāng)中,就會(huì)啟動(dòng)相應(yīng)的回收線程.相應(yīng)的初始化代碼如下所示:
1
2
3
4
5
6
7
8
9
10
|
static { ThreadGroup tg = Thread.currentThread().getThreadGroup(); for (ThreadGroup tgn = tg; tgn != null ; tg = tgn, tgn = tg.getParent()); Thread finalizer = new FinalizerThread(tg); finalizer.setPriority(Thread.MAX_PRIORITY - 2 ); finalizer.setDaemon( true ); finalizer.start(); } |
上面的static是靜態(tài)初始化塊,即只要類Finalizer被使用,即會(huì)觸發(fā)相應(yīng)的調(diào)用.這里使用的線程組是系統(tǒng)線程組,優(yōu)先級(jí)也還算高,被配置為后臺(tái)線程.
在使用jstack打印線程時(shí),出現(xiàn)的如圖下所示的線程,即是由這里來(lái)啟動(dòng)的.如下圖所示
總結(jié)
整個(gè)Finalizer即是通過(guò)finalReference,由JVM和相應(yīng)的java類相互配合來(lái)協(xié)同工作.并不是全部由jvm實(shí)現(xiàn),因此可以認(rèn)為其也并不是太底層的東西,而是為了實(shí)現(xiàn)相應(yīng)的語(yǔ)義.一切都是正常的java來(lái)完成,由jvm配合.了解到整個(gè)過(guò)程,也是對(duì)java本身的運(yùn)行機(jī)制有所了解.