Java對象死亡標記的過程
在Java可達性標記算法中,要宣告一個對象死亡,至少要進行兩次標記過程:
原理摘要
- 首先,如果發現對象從GC root出發不可達,那麼就會被第一次標記並進行篩選。篩選條件是此對象是否有必要執行finalize()方法。當對象沒有覆蓋finalize()方法,或者finalize()方法已經被執行過了,則會被判定爲“沒有必要執行”。
- 在確定對象有必要執行finalize()方法後,它將會被放入一個隊列中,並由一個較低級別的線程去執行此方法。稍後GC會對此隊列進行第二次標記,如果發現依舊從GC roots不可達,那麼此對象基本上是要被真的回收了。
- 也就是說,finalize()方法是對象逃脫死亡的最後一根稻草。如果對象可以在此方法中將自己建立起與GC roots 的可達性,也就是有其他人引用了此對象,那麼就可被免於清理
- 注意:finalize只會被執行一次!
代碼驗證
class MyObject {
public static MyObject sSaveHook;
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("finalize is executed!");
sSaveHook = this;
}
}
public class Main {
public static void main(String[] args) throws Exception {
MyObject.sSaveHook = new MyObject();
MyObject.sSaveHook = null;
//======第一次GC======
System.gc();
Thread.sleep(500); // 由於finalize方法優先級較低,所有暫停主線程0.5秒以等待它的執行
if(MyObject.sSaveHook != null){
System.out.println("object is alive!"); //這是第一次的輸出結果
} else {
System.out.println("object is dead");
}
//======第二次GC======
MyObject.sSaveHook = null;
System.gc();
Thread.sleep(500);
if(MyObject.sSaveHook != null){
System.out.println("object is alive!");
} else {
System.out.println("object is dead"); //這是第二次的輸出結果
}
}
}
第一次成功脫險,因爲調用finalize方法時這個對象重新找到了引用它的對象。第二次沒有,是因爲finalize方法只會被執行一次。也就說,第二次GC時沒有調用finalize方法,因此該對象對GC roots來說依舊不可達,結果被清除。
注意:由於實驗需要,筆者覆寫了finalize方法。但由於它的運行大家高昂,不確定性大,因此在實際應用中應避免finalize的使用。