finalize
基本預備相關知識?
對象的銷毀過程?
對象重生的例子?
對象的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交互了。?
對象的銷毀過程?
對象重生的例子?
對象的finalize的執行順序?
何時及如何使用finalize?
參考?
基本預備相關知識 ?
1 java的GC只負責內存相關的清理,所有其它資源的清理必須由程序員手工完成。要不然會引起資源泄露,有可能導致程序崩潰。?
2 調用GC并不保證GC實際執行。?
3 finalize拋出的未捕獲異常只會導致該對象的finalize執行退出。?
4 用戶可以自己調用對象的finalize方法,但是這種調用是正常的方法調用,和對象的銷毀過程無關。 ?
5 JVM保證在一個對象所占用的內存被回收之前,如果它實現了finalize方法,則該方法一定會被調用。Object的默認finalize什么都不做,為了效率,GC可以認為一個什么都不做的finalize不存在。?
6 對象的finalize調用鏈和clone調用鏈一樣,必須手工構造。?
如?
Java代碼??
對象的銷毀過程 ?
在對象的銷毀過程中,按照對象的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代碼??
期待輸出?
Java代碼??
但是有可能失敗,源于GC的不確定性以及時序問題,多跑幾次應該可以有成功的。詳細解釋見文末的參考文檔。?
對象的finalize的執行順序 ?
所有finalizable的對象的finalize的執行是不確定的,既不確定由哪個線程執行,也不確定執行的順序。?
考慮以下情況就明白為什么了,實例a,b,c是一組相互循環引用的finalizable對象。?
何時及如何使用finalize ?
從以上的分析得出,以下結論。?
1 最重要的,盡量不要用finalize,太復雜了,還是讓系統照管比較好。可以定義其它的方法來釋放非內存資源。?
2 如果用,盡量簡單。?
3 如果用,避免對象再生,這個是自己給自己找麻煩。?
4 可以用來保護非內存資源被釋放。即使我們定義了其它的方法來釋放非內存資源,但是其它人未必會調用該方法來釋放。在finalize里面可以檢查一下,如果沒有釋放就釋放好了,晚釋放總比不釋放好。?
5 即使對象的finalize已經運行了,不能保證該對象被銷毀。要實現一些保證對象徹底被銷毀時的動作,只能依賴于java.lang.ref里面的類和GC交互了。?
總結