1. 程式人生 > >深入理解jvm:物件的生存還是死亡

深入理解jvm:物件的生存還是死亡

即使在可達性分析演算法中不可達的物件,也並非是“非死不可”的,這時候它們暫時處於“緩刑”階段,要真正宣告一個物件死亡,至少要經歷兩次標記過程:如果物件在進行可達性分析後發現沒有與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我死了