Java詳解對象終止方法finalize()的用法
finalize()方法機制
Java 語言提供了對象終止(finalization)機制來允許開發(fā)人員提供對象被銷毀之前的自定義處理邏輯。
當(dāng)GC去回收垃圾時, 總會在即將回收之前調(diào)用這個對象的 finalize()方法 , 一個對象finalize()方法只會被調(diào)用一次
finalize()方法可以被重寫,通常在這個方法中進行一些資源釋放和清理的工作,比如關(guān)閉文件、套接字和數(shù)據(jù)庫連接等。
我們一般最好不要主動去調(diào)用對象的finalize()方法, 理由有以下三點 :
1.在 finalize()時可能會導(dǎo)致對象復(fù)活。
2.finalize()方法的執(zhí)行時間是沒有保障的,它完全由 GC 線程決定,極端情況下,若不發(fā)生 GC,則 finalize()方法將沒有執(zhí)行機會。
3.一個糟糕的 finalize()會嚴重影響 GC 的性能。比如 finalize 是個死循環(huán)。
為什么會有這種機制呢 ?
我們先來了解 jvm 為對象定義的三種狀態(tài)
第一次被 jvm 標為垃圾的對象此時處于"緩刑"階段, 也就是說它此時并不是非死不可的
可觸及的:從根節(jié)點開始,可以到達這個對象。
可復(fù)活的:對象的所有引用都被釋放,但是對象有可能在 finalize()中復(fù)活。
不可觸及的:對象的 finalize()被調(diào)用,并且沒有復(fù)活,那么就會進入不可觸及狀態(tài)。不可觸及的對象不可能被復(fù)活,因為 finalize()只會被調(diào)用一次。
以上 3 種狀態(tài)中,是由于 finalize()方法的存在,進行的區(qū)分。只有在對象不可觸及時才可以被回收
可觸及的, 意思就是說, 對象此時存在引用鏈, 是存活的, 可復(fù)活的意思是說, 此對象雖然已經(jīng)被GC標為了垃圾, 但是此時未調(diào)用 finalize() 方法, 這個對象是有可能在finalize()中復(fù)活的. 不可觸及的就是說, 此時finalize()方法已經(jīng)被調(diào)用過了(沒有復(fù)活), 這個對象最終的命運已經(jīng)是非死不可了, 只能靜等GC去回收它
那么具體的過程是怎樣的呢?
判定一個對象 objA 是否可回收,至少要經(jīng)歷兩次標記過程:
1.如果對象 objA 到 GC Roots 沒有引用鏈,則進行第一次標記。
2.進行篩選,判斷此對象是否有必要執(zhí)行 finalize()方法
如果對象 objA 沒有重寫 finalize()方法,或者 finalize()方法已經(jīng)被虛擬機調(diào)用過,則虛擬機視為“沒有必要執(zhí)行”,objA 被判定為不可觸及的。
如果對象 objA 重寫了 finalize()方法,且還未執(zhí)行過,那么 objA 會被插入到 F-Queue 隊列中,由一個虛擬機自動創(chuàng)建的、低優(yōu)先級的 Finalizer 線程觸發(fā)其 finalize()方法執(zhí)行。finalize()方法是對象逃脫死亡的最后機會,稍后 GC 會對 F-Queue 隊列中的對象進行第二次標記。如果 objA 在 finalize()方法中與引用鏈上的任何一個對象建立了聯(lián)系,那么在第二次標記時,objA 會被移出“即將回收”集合。之后,對象會再次出現(xiàn)沒有引用存在的情況. 在這個情況下,finalize()方法不會被再次調(diào)用,對象會直接變成不可觸及的狀態(tài),也就是說,一個對象的 finalize()方法只會被調(diào)用一次。
接著我們用代碼演示對象的復(fù)活
public class CanReliveObj { public static CanReliveObj obj;//類變量,屬于 GC Root //此方法只能被調(diào)用一次 @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("調(diào)用當(dāng)前類重寫的finalize()方法"); obj = this;//當(dāng)前待回收的對象在finalize()方法中與引用鏈上的一個對象obj建立了聯(lián)系 } public static void main(String[] args) { try { obj = new CanReliveObj(); // 對象第一次成功拯救自己 obj = null; System.gc();//調(diào)用垃圾回收器 System.out.println("第1次 gc"); // 因為Finalizer線程優(yōu)先級很低,暫停2秒,以等待它 Thread.sleep(2000); if (obj == null) { System.out.println("obj is dead"); } else { System.out.println("obj is still alive"); } System.out.println("第2次 gc"); // 下面這段代碼與上面的完全相同,但是這次自救卻失敗了 obj = null; System.gc(); // 因為Finalizer線程優(yōu)先級很低,暫停2秒,以等待它 Thread.sleep(2000); if (obj == null) { System.out.println("obj is dead"); } else { System.out.println("obj is still alive"); } } catch (InterruptedException e) { e.printStackTrace(); } } }
執(zhí)行結(jié)果 :
先將引用指向 null , 這時第一次GC , 我們重寫了finalize()方法, 致使對象在第一次垃圾回收時成功自救, 第二次再將引用指向null , 因為finalize() 方法只會被執(zhí)行一次, 這時對象只能等待死亡
到此這篇關(guān)于Java詳解對象終止方法finalize()的用法的文章就介紹到這了,更多相關(guān)Java finalize()內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot中整合Ehcache實現(xiàn)熱點數(shù)據(jù)緩存的詳細過程
這篇文章主要介紹了SpringBoot中整合Ehcache實現(xiàn)熱點數(shù)據(jù)緩存,SpringBoot 中使用 Ehcache 比較簡單,只需要簡單配置,說白了還是 Spring Cache 的用法,合理使用緩存機制,可以很好地提高項目的響應(yīng)速度,需要的朋友可以參考下2023-04-04APT?注解處理器實現(xiàn)?Lombok?常用注解功能詳解
這篇文章主要為大家介紹了使用APT?注解處理器實現(xiàn)?Lombok?常用注解功能詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-09-09JavaEE開發(fā)之SpringMVC中的自定義消息轉(zhuǎn)換器與文件上傳
本篇文章主要介紹了SpringMVC的相關(guān)知識。同時也會介紹到j(luò)s、css這些靜態(tài)文件的加載配置,以及服務(wù)器推送的兩種實現(xiàn)方式并且給出了兩者的區(qū)別。下面跟著小編一起來看下吧2017-04-04IDEA2023版本創(chuàng)建Spring項目只能勾選17和21卻無法使用Java8的完美解決方案
想創(chuàng)建一個springboot的項目,本地安裝的是1.8,但是在使用Spring Initializr創(chuàng)建項目時,發(fā)現(xiàn)版本只有17和21,這篇文章主要介紹了IDEA2023版本創(chuàng)建Sping項目只能勾選17和21,卻無法使用Java8的解決方法,需要的朋友可以參考下2023-12-12java多線程學(xué)習(xí)之死鎖的模擬和避免(實例講解)
下面小編就為大家?guī)硪黄猨ava多線程學(xué)習(xí)之死鎖的模擬和避免(實例講解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06如何基于Springboot完成新增員工功能并設(shè)置全局異常處理器
最近工作中遇到了做一個管理員工信息的功能,下面這篇文章主要給大家介紹了關(guān)于如何基于Springboot完成新增員工功能并設(shè)置全局異常處理器的相關(guān)資料,文中通過圖文介紹的非常詳細,需要的朋友可以參考下2022-11-11