深入理解jvm:物件的生存還是死亡
阿新 • • 發佈:2019-01-22
即使在可達性分析演算法中不可達的物件,也並非是“非死不可”的,這時候它們暫時處於“緩刑”階段,要真正宣告一個物件死亡,至少要經歷兩次標記過程:如果物件在進行可達性分析後發現沒有與GC Roots 相連線的引用鏈,那它將會被第一次標記並且進行一次篩選,篩選的條件是此物件是否有必要執行finalize() 方法。當物件沒有覆蓋finalize() 方法,或者finalize() 方法已經被虛擬機器呼叫過,虛擬機器將這兩種情況都視為“沒有必要執行”。
如果這個物件被判定為有必要執行finalize()方法,那麼這個物件將會放置在一個叫做F-Queuc的佇列之中,並在稍後由一個由虛擬機器自動建立的、低優先順序的Finalizer執行緒去執行它。這裡所謂的“執行”是指虛擬機器會觸發這個方法,但並不承諾會等待它執行結束,這樣做的原因是如果一個物件在finalizeO 方法中執行緩慢,或者發生了死迴圈(更極端的情況),將很可能會導致F-Queue佇列中其他物件永久處於等待,甚至導致整個記憶體回收系統崩潰。finalize() 方法是物件逃脫死亡命運的最後一次機公稍後GC將對F-QUCUC中的物件進行第二次小規模的標記,如果物件要在finalize()中成功揚救自己一隻要重新與引用鏈上的任何- 一個物件建立關聯即可,譬如把自己(this 關鍵字) 賦值給某個類變數或者物件的成員變數,那在第二次標記時它將被移除出“即將回收”的集合: 如果物件這時候還沒有逃脫,那基本上它就真的被回收了。從程式碼清單3-2 中我們可以看到一個物件的finalize()被執行,但是它仍然可以存活。程式碼清單3-2一次物件自我拯救的演示。
package ccc; /*此程式碼演示了兩點 * 物件可以在GC時自我拯救 * 這種自救只會有一次,因為一個物件的finalize方法只會被自動呼叫一次 * */ public class FinalizeEscapeGC { public static FinalizeEscapeGC SAVE_HOOK=null; public void isAlive(){ System.out.println("yes我還活著"); } public void finalize() throws Throwable{ super.finalize(); System.out.println("執行finalize方法"); FinalizeEscapeGC.SAVE_HOOK=this;//自救 } public static void main(String[] args) throws InterruptedException{ SAVE_HOOK=new FinalizeEscapeGC(); //物件的第一次回收 SAVE_HOOK=null; System.gc(); //因為finalize方法的優先順序很低所以暫停0.5秒等它 Thread.sleep(500); if(SAVE_HOOK!=null){ SAVE_HOOK.isAlive(); }else{ System.out.println("no我死了"); } //下面的程式碼和上面的一樣,但是這次自救卻失敗了 //物件的第一次回收 SAVE_HOOK=null; System.gc(); Thread.sleep(500); if(SAVE_HOOK!=null){ SAVE_HOOK.isAlive(); }else{ System.out.println("no我死了"); } } }
結果:
執行finalize方法
yes我還活著
no我死了