閑逛ITEye時(shí)看到了譯帝的一篇翻譯博客,其中提到了關(guān)于Java類重寫finalize方法后帶來的詭異的GC overhead limit問題黎休。博客的結(jié)尾非常詳細(xì)的說明了這個(gè)問題產(chǎn)生的原理米者,但是始終有一個(gè)地方?jīng)]有得到清晰的答案:由于finalize方法是Object類的protected方法花颗,即無論重寫與否大刊,所有的Java類都會(huì)帶有finalize方法淀弹,但為什么只有重寫之后才會(huì)出現(xiàn)GC問題夫壁,不重寫與重寫的真實(shí)差別到底在哪兒拾枣?
通過思考始終得不到答案,索性打開Eclipse直接調(diào)試代碼:
首先驗(yàn)證了博客中的現(xiàn)象的確可以重現(xiàn)掌唾。
中間一度懷疑問題的原因可能是finalize方法中引用了類的靜態(tài)變量AtomicInteger會(huì)引起GC的問題(實(shí)際上這個(gè)方向是錯(cuò)誤的放前,GC是根據(jù)對(duì)象是否存在被引用關(guān)系來判斷對(duì)象是否回收,慚愧)糯彬,刪掉原來的方法體然后隨意在finalize方法中聲明了一個(gè)變量凭语,結(jié)果運(yùn)行時(shí)問題仍然存在。
到了此時(shí)撩扒,開始懷疑是不是和finalize方法體有關(guān)系了似扔,直接刪除掉finalize方法體中的所有內(nèi)容吨些,運(yùn)行后發(fā)現(xiàn)問題真的不存在了!
由于出現(xiàn)GC問題時(shí)內(nèi)存堆中存在大量Finalizer對(duì)象炒辉,而Finalizer對(duì)象只能通過Finalizer類的靜態(tài)register方法創(chuàng)建豪墅,因此嘗試在register方法中加上斷點(diǎn)對(duì)兩種情況分別運(yùn)行,發(fā)現(xiàn)方法體為空時(shí)黔寇,register方法不會(huì)被調(diào)用偶器,這樣自定義對(duì)象便沒有被任何對(duì)象引用,可以輕松的被GC回收掉缝裤。
由于register方法是被VM所調(diào)用屏轰,只能求助于莫樞大神,而莫樞也確認(rèn)了只要Java類以及它所有的祖先類中不含有finalize方法或者finalize方法體為空時(shí)憋飞,VM便不會(huì)將該類的對(duì)象實(shí)例注冊(cè)為finalizable對(duì)象(地址)霎苗。
至此,算是把整個(gè)問題的來龍去脈給整理的差不多了:
JVM創(chuàng)建自定義對(duì)象榛做。
JVM檢測(cè)對(duì)象類以及祖先類是否含有非空的finalize方法定義唁盏,如果均沒有,則不進(jìn)行后續(xù)的Finalizer相關(guān)處理检眯。
JVM調(diào)用Finalizer類的靜態(tài)register方法厘擂,創(chuàng)建包含該對(duì)象引用的Finalizer對(duì)象,同時(shí)Finalizer靜態(tài)類將該Finalizer對(duì)象加入到一個(gè)單向鏈表中轰传。
運(yùn)行過程中驴党,JVM會(huì)將這些Finalizer對(duì)象的狀態(tài)更新為pending(這部分可以參考java.lang.ref.Reference類中的說明)。
Reference類中的ReferenceHandler線程會(huì)掃描到這些狀態(tài)為pending的Finalizer對(duì)象获茬,將這些對(duì)象enqueue到Finalizer靜態(tài)類引用的ReferenceQueue(非Finalizer中的單向鏈表)當(dāng)中。
Finalizer線程從ReferenceQueue中逐一彈出Finalizer對(duì)象倔既,首先將Finalizer對(duì)象從Finalizer的單向鏈表中刪除恕曲,解除了Finalizer靜態(tài)類對(duì)Finalizer對(duì)象的引用關(guān)系,之后調(diào)用Finalizer對(duì)象引用的自定義對(duì)象的finalize方法渤涌,F(xiàn)inalizer對(duì)象以及自定義對(duì)象此時(shí)均可被GC回收佩谣。
由于Finalizer線程的低優(yōu)先級(jí),可能引起舊對(duì)象的釋放速度無法跟上新對(duì)象的創(chuàng)建速度实蓬,引起OutOfMemory問題(例子中的GC overhead limit原因是由于新對(duì)象創(chuàng)建的代價(jià)太低而舊對(duì)象回收的代價(jià)較高導(dǎo)致CPU用于GC回收的時(shí)間比例超過98%)
最后茸俭,網(wǎng)易研究院的馬進(jìn)在他的博客中非常詳細(xì)的闡述了finalize的原理以及因此引發(fā)的案例,也非常值得一讀安皱。