1. 程式人生 > 其它 >【深入理解Java虛擬機器】物件自我拯救

【深入理解Java虛擬機器】物件自我拯救

  即使在可達性分析演算法中不可達的物件,也並不是“非死不可”。

  要宣告一個物件死亡,至少要經過兩次標記過程:如果物件在進行可達性分析後發現沒有與GC Roots相連線的引用鏈,那它將會被第一次標記並且進行一次篩選,篩選的條件是此物件是否有必要執行finalize()方法。當物件沒有覆蓋finalize()方法,或者finalize()方法已經被虛擬機器呼叫過,虛擬機器將這兩種情況都視為“沒有必要執行”。

  如果這個物件被判定為有必要執行finalize()方法,那麼這個物件將會放置在一個叫做F-Queue的佇列之中,並在稍後由一個由虛擬機器自動建立的、低優先順序的Finalizer執行緒去執行它。這裡所謂的“執行”是指虛擬機器會觸發這個方法,但並不承諾會等待它執行結束,這樣做的原因是,如果一個物件在finalize()中執行緩慢,或者發生了死迴圈,將可能會導致F-Queue佇列中其他物件永久處於等待,甚至導致整個記憶體回收系統崩潰。finalize()方法是物件逃脫死亡命運的最後一次機會,稍後GC將對F-Queue中的物件進行第二次小規模的標記,如果物件在finalize()中成功拯救自己----只要重新與引用鏈上的任何一個物件建立關聯即可,譬如把自己(this關鍵字)賦值給某個類變數或者物件的成員變數,那在第二次標記時它將被移除出“即將回收”的集合;如果物件這時候沒有逃脫,那基本上它就真的被回收了。

public class FinalizeEscapeGC {

    public static FinalizeEscapeGC SACE_HOOK = null;

    public void isAlive(){
        System.out.println("yes,i'm alive.");
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize method executed!");
       FinalizeEscapeGC.SACE_HOOK = this;
    }

    public static void main(String[] args) throws Exception{
        SACE_HOOK = new FinalizeEscapeGC();

        // 物件第一次成功拯救自己
        SACE_HOOK = null;
        System.gc();

        // 因為finalize方法優先順序很低,所以暫停0.5秒以等待它
        Thread.sleep(500);

        if (SACE_HOOK != null) {
            SACE_HOOK.isAlive();
        } else {
            System.out.println("no, i'm dead-1.");
        }

        // 下面這段程式碼與上面完全相同,但這次自救卻失敗了
        SACE_HOOK = null;
        System.gc();

        // 因為finalize方法優先順序很低,所以暫停0.5秒以等待它
        Thread.sleep(500);

        if (SACE_HOOK != null) {
            SACE_HOOK.isAlive();
        } else {
            System.out.println("no, i'm dead-2.");
        }

    }
}

執行結果:

finalize method executed!
yes,i'm alive.
no, i'm dead-2.
// 注:
// 儘量避免使用finalize()方法拯救物件
// finalize()方法執行代價高昂,不確定性大,無法保證各個物件的呼叫順序。
// finalize()方法能做的所有工作,使用try-catch或者其他方式都可以做得更好、更及時。