深入理解java的finalize、GC、close()的優劣

來源:互聯網
上載者:User
目錄 


基本預備相關知識 
對象的銷毀過程 
對象重生的例子 
對象的finalize的執行順序 
何時及如何使用finalize 
參考 


基本預備相關知識 


1 java的GC只負責記憶體相關的清理,所有其它資源的清理必須由程式員手工完成。要不然會引起資源流失,有可能導致程式崩潰。 


2 調用GC並不保證GC實際執行。 


3 finalize拋出的未捕獲異常只會導致該對象的finalize執行退出。 


4 使用者可以自己調用對象的finalize方法,但是這種調用是正常的方法調用,和對象的銷毀過程無關。 


5 JVM保證在一個對象所佔用的記憶體被回收之前,如果它實現了finalize方法,則該方法一定會被調用。Object的預設finalize什麼都不做,為了效率,GC可以認為一個什麼都不做的finalize不存在。 


6 對象的finalize調用鏈和clone調用鏈一樣,必須手工構造。 
如 
Java代碼   protected void finalize() throws Throwable {       super.finalize();   }  

對象的銷毀過程 


在對象的銷毀過程中,按照對象的finalize的執行情況,可以分為以下幾種,系統會記錄對象的對應狀態: 
unfinalized 沒有執行finalize,系統也不準備執行。 
finalizable 可以執行finalize了,系統會在隨後的某個時間執行finalize。 
finalized 該對象的finalize已經被執行了。 


GC怎麼來保持對finalizable的對象的追蹤呢。GC有一個Queue,叫做F-Queue,所有對象在變為finalizable的時候會加入到該Queue,然後等待GC執行它的finalize方法。 


這時我們引入了對對象的另外一種記錄分類,系統可以檢查到一個對象屬於哪一種。 
reachable 從活動的對象引用鏈可以到達的對象。包括所有線程當前棧的局部變數,所有的靜態變數等等。 
finalizer-reachable 除了reachable外,從F-Queue可以通過引用到達的對象。 
unreachable 其它的對象。 


來看看對象的狀態轉換圖。 

 

好大,好暈,慢慢看。 


1 首先,所有的對象都是從Reachable+Unfinalized走向死亡之路的。 


2 當從當前活動集到對象不可達時,對象可以從Reachable狀態變到F-Reachable或者Unreachable狀態。 


3 當對象為非Reachable+Unfinalized時,GC會把它移入F-Queue,狀態變為F-Reachable+Finalizable。 


4 好了,關鍵的來了,任何時候,GC都可以從F-Queue中拿到一個Finalizable的對象,標記它為Finalized,然後執行它的finalize方法,由於該對象在這個線程中又可達了,於是該對象變成Reachable了(並且Finalized)。而finalize方法執行時,又有可能把其它的F-Reachable的對象變為一個Reachable的,這個叫做對象再生。 


5 當一個對象在Unreachable+Unfinalized時,如果該對象使用的是預設的Object的finalize,或者雖然重寫了,但是新的實現什麼也不幹。為了效能,GC可以把該對象之間變到Reclaimed狀態直接銷毀,而不用加入到F-Queue等待GC做進一步處理。 


6 從狀態圖看出,不管怎麼折騰,任意一個對象的finalize只至多執行一次,一旦對象變為Finalized,就怎麼也不會在回到F-Queue去了。當然沒有機會再執行finalize了。 


7 當對象處於Unreachable+Finalized時,該對象離真正的死亡不遠了。GC可以安全的回收該對象的記憶體了。進入Reclaimed。 




對象重生的例子 
Java代碼   class C {       static A a;   }      class A {       B b;          public A(B b) {           this.b = b;       }          @Override       public void finalize() {           System.out.println("A finalize");           C.a = this;       }   }      class B {       String name;       int age;          public B(String name, int age) {           this.name = name;           this.age = age;       }          @Override       public void finalize() {           System.out.println("B finalize");       }          @Override       public String toString() {           return name + " is " + age;       }   }      public class Main {       public static void main(String[] args) throws Exception {           A a = new A(new B("allen", 20));           a = null;              System.gc();           Thread.sleep(5000);           System.out.println(C.a.b);       }   }  

期待輸出 


Java代碼   A finalize   B finalize   allen is 20   但是有可能失敗,源於GC的不確定性以及時序問題,多跑幾次應該可以有成功的。詳細解釋見文末的參考文檔。 


對象的finalize的執行順序 


所有finalizable的對象的finalize的執行是不確定的,既不確定由哪個線程執行,也不確定執行的順序。 
考慮以下情況就明白為什麼了,執行個體a,b,c是一組相互循環參考的finalizable對象。 


何時及如何使用finalize 


從以上的分析得出,以下結論。 
1 最重要的,盡量不要用finalize,太複雜了,還是讓系統照管比較好。可以定義其它的方法來釋放非記憶體資源。 
2 如果用,盡量簡單。 
3 如果用,避免對象再生,這個是自己給自己找麻煩。 
4 可以用來保護非記憶體資源被釋放。即使我們定義了其它的方法來釋放非記憶體資源,但是其它人未必會調用該方法來釋放。在finalize裡面可以檢查一下,如果沒有釋放就釋放好了,晚釋放總比不釋放好。 
5 即使對象的finalize已經運行了,不能保證該對象被銷毀。要實現一些保證對象徹底被銷毀時的動作,只能依賴於java.lang.ref裡面的類和GC互動了。 


參考 
1、作者:zhang_xzhi_xjtu
網址:http://www.iteye.com/topic/484934
2、關於參考型別,GC,finalize的相互互動可以參考ReferenceQueue GC finalize Reference 測試及相關問題

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.