【查缺補漏】finalize方法與GC

一、finalize()的作用

finalize() 是 Object 的 protected 方法,子類可以覆蓋該方法以實現資源清理工作,GC在回收對象之前調用該方法。
finalize() 與 C++ 中的析構函數不是對應的。C++ 中的析構函數調用的時機是確定的(對象離開作用域或 delete 掉),但 Java 中的 finalize 的調用具有不確定性。

二、finalize()執行的生命週期

finalize流程:當對象變成 GC Roots 不可達時,GC 會判斷該對象是否覆蓋了 finalize 方法,若未覆蓋,則直接將其回收。否則,若對象未執行過 finalize 方法,將其放入 F-Queue 隊列,由一低優先級線程執行該隊列中對象的 finalize 方法,執行 finalize 方法完畢後,GC 會再次判斷該對象是否可達,若不可達,則進行回收,否則,對象“復活”。

對象可由兩種狀態,涉及到兩類狀態空間,一是終結狀態空間 F = {unfinalized, finalizable, finalized};二是可達狀態空間 R = {reachable, finalizer-reachable, unreachable}。各狀態含義如下:

  • unfinalized:新建對象會先進入此狀態,GC 並未準備執行其 finalize 方法,因爲該對象是可達的
  • finalizable:表示 GC 可對該對象執行 finalize 方法,GC 已檢測到該對象不可達
  • finalized:表示 GC 已經對該對象執行過 finalize 方法
  • reachable:表示 GC Roots 引用可達
  • finalizer-reachable(f-reachable):表示不是reachable,但可通過某個finalizable對象可達
  • unreachable:對象不可通過上面兩種途徑可達
    狀態流轉

具體狀態轉換:

  1. 新建對象首先處於[reachable, unfinalized]狀態(A)
  2. 隨着程序的運行,一些引用關係會消失,導致狀態變遷,從reachable狀態變遷到 f-reachable(B, C, D)或 unreachable(E, F)狀態。
  3. 若 JVM 檢測到處於 unfinalized 狀態的對象變成 f-reachable 或 unreachable,JVM 會將其標記爲 finalizable 狀態(G,H)。若對象原處於[unreachable, unfinalized]狀態,則同時將其標記爲 f-reachable(H)。
  4. 在某個時刻,JVM 取出某個 finalizable 對象,將其標記爲 finalized 並在某個線程中執行其finalize 方法。由於是在活動線程中引用了該對象,該對象將變遷到(reachable, finalized)狀態(K或J)。該動作將影響某些其他對象從 f-reachable 狀態重新回到 reachable 狀態(L, M, N), 這就是對象重生。
  5. 處於 finalizable 狀態的對象不能同時是 unreahable 的,由第4點可知,將對象 finalizable 對象標記爲 finalized 時會由某個線程執行該對象的 finalize 方法,致使其變成 reachable。這也是圖中只有八個狀態點的原因。
  6. 程序員手動調用 finalize 方法並不會影響到上述內部標記的變化,因此 JVM 只會至多調用 finalize 一次,即使該對象“復活”也是如此。程序員手動調用多少次不影響 JVM 的行爲。
  7. 若JVM檢測到finalized狀態的對象變成unreachable,回收其內存(I)。
  8. 若對象並未覆蓋finalize方法,JVM會進行優化,直接回收對象(O)。
  9. System.runFinalizersOnExit()等方法可以使對象即使處於reachable狀態,JVM仍對其執行finalize方法。

三、代碼示例

public class GC {

    public static GC SAVE_HOOK = null;

    public static void main(String[] args) throws InterruptedException {
    	//新建對象,對象此時的狀態是(reachable,unfinalized)
        SAVE_HOOK = new GC();
        
        //將SAVE_HOOK設置成null,此時剛纔創建的對象就不可達了,因爲沒有句柄再指向它了,對象此時狀態是(unreachable,unfinalized)
        SAVE_HOOK = null;
        
        //強制系統執行垃圾回收,系統發現剛纔創建的對象處於unreachable狀態,
        //並檢測到這個對象的類覆蓋了finalize方法,因此把這個對象放入F-Queue隊列,
        //由低優先級線程執行它的finalize方法,
        //此時對象的狀態變成(unreachable, finalizable)或者是(finalizer-reachable,finalizable)
        System.gc();
        
        //sleep,目的是給低優先級線程從F-Queue隊列取出對象並執行其finalize方法提供機會。
        //在執行完對象的finalize方法中的super.finalize()時,對象的狀態變成(unreachable,finalized)狀態,
        //但在覆蓋的finalize方法中又執行了SAVE_HOOK = this;
        //又有句柄指向這個對象了,對象又可達了。因此對象的狀態又變成了(reachable, finalized)狀態。
        Thread.sleep(500);
        if (null != SAVE_HOOK) { //此時對象應該處於(reachable, finalized)狀態  
        	//這句話會輸出,注意對象由unreachable,經過finalize復活了。
            System.out.println("Yes , I am still alive");
        } else {
            System.out.println("No , I am dead");
        }
        //再一次將SAVE_HOOK放空,此時剛纔復活的對象,狀態變成(unreachable,finalized)
        SAVE_HOOK = null;
        //再一次強制系統回收垃圾,此時系統發現對象不可達,雖然覆蓋了finalize方法,但已經執行過了,因此直接回收。
        System.gc();
        //爲系統回收垃圾提供時間
        Thread.sleep(500);
        if (null != SAVE_HOOK) { //此時對象應該處於(unreachable, finalized)狀態  
            System.out.println("Yes , I am still alive");
        } else {
        	//輸出這句
            System.out.println("No , I am dead");
        }
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("execute method finalize()");
        SAVE_HOOK = this;
    }
}

輸出結果:

execute method finalize()
Yes , I am still alive
No , I am dead
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章