JVM——對象的回收

本篇來自周志明的<<深入理解java虛擬機>>

預備知識: 如何判定對象存在活引用? https://blog.csdn.net/q5706503/article/details/84899581

即使在可達性分析算法中不可達的對象,也並非是“非死不可”的 ,這時候它們暫時處於“緩刑” 階段 ,要真正宣告一個對象死亡 ,至少要經歷兩次標記過程 : 如 果對象在進行可達性分析後發現沒有與GC Roots相連接的引用鏈,那它將會被第一次標記並且進行一次篩選, 篩選的條件是此對象是否有必要執行finalize() 方法。當對象沒有覆蓋finalize() 方法,或 者finalize() 方法已經被虛擬機調用過,虛擬機將這兩種情況都視爲“沒有必要執行”。

如果這個對象被判定爲有必要執行finalize() 方法,那麼這個對象將會放置在一個叫做 F-Queue的隊列之中,並在稍後由一個由虛擬機自動建立的、低優先級的Finalizer線程去執行它。這裏所謂的“執行”是指虛擬機會觸發這個方法,但並不承諾會等待它運行結束,這樣做的原因是,如果一個對象在finalize() 方法中執行緩慢,或者發生了死循環(更極端的情況 ),將很可能會導致F-Queue隊列中其他對象永久處於等待,甚至導致整個內存回收系統崩潰。finalize() 方法是對象逃脫死亡命運的最後一次機會,稍後GC將對F-Queue中的對象進行第二次小規模的標記,如果對象要在finalize ( ) 中成功拯救自己——只要重新與引用鏈 上的任何一個對象建立關聯即可,譬如把自己(this關鍵字)賦值給某個類變量或者對象的成員變 量 ,那在第二次標記時它將被移除出“即將回收” 的集合 ;如果對象這時候還沒有逃脫,那基本上它就真的被回收了。從代碼清單3-2中我們可以看到一個對象的finalize()被執行 ,但是它仍然可以存活。
 

/**
 * 此代碼演示了兩點: 
 * 1.對象可以在被GC時自我拯救。 
 * 2.這種自救的機會只有一次,因爲一個對象的finalize()方法最多隻會被系統自動調用一次
 * @author zzm
 */
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 mehtod executed!");
        FinalizeEscapeGC.SAVE_HOOK = this;
    }
 
    public static void main(String[] args) throws Throwable {
        SAVE_HOOK = new FinalizeEscapeGC();
 
        //對象第一次成功拯救自己
        SAVE_HOOK = null;
        System.gc();
        // 因爲Finalizer方法優先級很低,暫停0.5秒,以等待它
        Thread.sleep(500);
        if (SAVE_HOOK != null) {
            SAVE_HOOK.isAlive();
        } else {
            System.out.println("no, i am dead :(");
        }
 
        // 下面這段代碼與上面的完全相同,但是這次自救卻失敗了
        SAVE_HOOK = null;
        System.gc();
        // 因爲Finalizer方法優先級很低,暫停0.5秒,以等待它
        Thread.sleep(500);
        if (SAVE_HOOK != null) {
            SAVE_HOOK.isAlive();
        } else {
            System.out.println("no, i am dead :(");
        }
    }
}

運行結果: 

finalize mehtod executed ! 
yes,i am still alive : ) 
no,i am dead : (

從代碼清單的運行結果可以看出,SAVE_HOOK對象的finalize ( ) 方法確實被GC收集器觸發過,並且在被收集前成功逃脫了。

另一個值得注意的地方是,代碼中有兩段完全一樣的代碼片段,執行結果卻是一次逃脫成功,一次失敗,這是因爲任何一個對象的finalize()方法都只會被系統自動調用一次, 如果對象面臨下一次回收,它的finalize() 方法不會被再次執行,因此第二段代碼的自救行動失敗了。

需要特別說明的是,上面關於對象死亡時finalize ( ) 方法的描述可能帶有悲情的藝術色彩 ,筆者並不鼓勵大家使用這種方法來拯救對象。相反 ,筆者建議大家儘量避免使用它,因爲它不是C/C++中的析構函數,而是Java剛誕生時爲了使C/C++程序員更容易接受它所做出的一個妥協。它的運行代價高昂,不確定性大,無法保證各個對象的調用順序。有些教材中描述它適合做“關閉外部資源”之類的工作,這完全是對這個方法用途的一種自我安慰。 finalize ( ) 能做的所有工作,使用try-finally或者其他方式都可以做得更好、更及時,所以筆者建議大家完全可以忘掉Java語言中有這個方法的存在。
 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章