1. 程式人生 > >GC判斷可回收物件演算法

GC判斷可回收物件演算法

宣告:本文摘錄周志明先生的《深入理解Java虛擬機器》,記錄下GC演算法中的可達性演算法分析。是在問答頻道看到一個對可達性演算法的疑問,梳理下,也是自我學習鞏固的過程。關於可達性演算法的幾點記錄如下:

   首先,可達性演算法基本思路是定義一些列稱為"GC-Roots"的物件作為起始階段,從這些節點向下搜尋,搜尋走過的路徑稱為引用鏈,當一個物件到GCRoots沒有任何引用鏈時(即從GCRoots到這個物件不可達),則證明此物件是不可用的。
    其次,可達性演算法中的不可達物件,在真正宣告“死亡”需要回收之前,至少要經過兩輪標記過程:如果物件不可達,會被第一次標記並且進行一次篩選,篩選條件是此物件有沒有必要執行finalize()方法。當物件沒有覆蓋finalize()方法或者這個方法以及被執行過了,那麼就視為沒有必要再執行。
   對於那些有必要執行finalize()方法的物件會被放在一個佇列F-Queue中,稍後由虛擬機器的一個執行緒去執行逐一執行佇列中物件的finalize方法,如果執行緒在執行過程中發生了死迴圈,或者某個物件的finalize方法執行時間過長,導致佇列中其他物件一直處於等待,那麼就會導致整個記憶體回收系統崩潰。
   第三,finalize()方法是物件逃脫死亡命運的最後一次機會,稍後GC會對F-Queue中的物件進行第二次小規模的標記,如果能在finalize中成功重新引用,第二次標記時就會將該物件從F-Queue集合中移除,而成功脫逃。
   所以,我理解的迴圈引用可能是想說不可達後突然又再次引用了,那麼只能在finalize方法中再次引用而救活物件了。如果是普通迴圈中的迴圈操作引用,應該還沒有涉及到垃圾回收、標記不可達的時候。
   最後,演算法中的根物件通常是全域性的靜態成員物件,方法區中的常量引用物件,Native引用物件,執行緒棧中的引用物件等。
   附上一個可達性演算法中笫二次標記自我復活的例子:
/**
 * 自我救贖的機會只有一次,即重新finalize並再次引用自己,
 * 因為一個物件的 finalize()方法最多隻會被系統呼叫一次
 *
 */
public class FinalizeEscapeGC {
    public static FinalizeEscapeGC save_hook = null;

    public void isAlive() {
        System.out.println("yes ,I am still alive.");
    }

    @Override
    protected void finalize() throws
Throwable { super.finalize(); System.out.println("finalize method executd!"); FinalizeEscapeGC.save_hook = this; } public static void main(String[] args) throws Throwable { save_hook = new FinalizeEscapeGC(); // 設定為null,標記不可達,通知GC回收 save_hook = null
; System.gc(); // 當前執行緒休眠5秒,讓GC執行緒優先執行 Thread.sleep(500); if (save_hook != null) { //finalize()中自引用成功,該分支執行了 save_hook.isAlive(); } else { System.out.println("first .Oh My God ,I am going to dead.Who can help me?"); } // 再測試一次:下面程式碼與上面完全相同,但是自我救贖失敗,因為finalize只會執行一次 save_hook = null; System.gc(); // 當前執行緒休眠5秒,讓GC執行緒優先執行 Thread.sleep(500); if (save_hook != null) { save_hook.isAlive(); } else { //已經執行過finalize()了,不能再逃脫回收命運了 System.out.println("second Oh My God ,I am going to dead.Who can help me?"); } } }

測試結果可以看到物件能夠完成一次在finalize中的自我復活,但是也僅有這一次機會,最終還是逃不脫GC的命運。