Java語言提供了對象終止(finalization)機制來允許開發人員自定義對象被銷毀之前的處理邏輯。當垃圾回收器發現沒有引用指向一個對象時,通常接下來要做的就是垃圾回收,即清除該對象,而finalization機制使得在清除此對象之前,總會先調用這個對象的finalize()方法。finalize()方法允許在子類中被重寫,用于在對象被回收時進行資源釋放或清理相關內存,例如關閉文件、套接字和數據庫連接等。但是,不要過分依賴對象的finalize()方法來釋放資源,最好有其他的方法來釋放資源,例如手動調用close()方法,理由如下。(1)在調用finalize()方法時可能會導致對象復活,即在finalize()方法中當前對象this又被賦值給了一個有效的變量引用。(2)一個糟糕的finalize()會嚴重影響GC的性能,而長時間的GC是會影響程序運行性能和體驗的。(3)finalize()方法的執行時間是沒有保障的,它完全由GC線程決定,極端情況下,若不發生GC,則finalize()方法將沒有執行機會。另外,finalize()方法工作效率很低。如果一個對象在回收前需要調用finalize()方法的話,要先將其加入一個隊列,之后由Finalizer線程處理這些對象,而這個線程的優先級非常低,所以很難被CPU執行到,進而導致對象的finalize()方法遲遲不能被執行,資源遲遲不能被釋放,對象遲遲不能被垃圾回收。從功能上來說,finalize()方法與C++中的析構函數比較相似,都是用來做清理善后的工作。只不過C++中需要手動調用析構函數清理內存,而Java采用的是基于垃圾回收器的自動內存管理機制。finalize()方法在本質上不同于C++中的析構函數。由于finalize()方法的存在,JVM中的對象一般處于三種可能的狀態。如果從所有的根節點都無法訪問到某個對象,說明該對象已經不再使用了。一般來說,此對象需要被回收。但事實上,也并非是“非死不可”的,這時候它們暫時處于“緩刑”階段。一個無法觸及的對象有可能在某一個條件下“復活”自己,如果這樣,那么對它的回收就是不合理的,為此,定義JVM中的對象可能的三種狀態。
(1)可觸及的:從根節點開始,可以到達這個對象
(2)可復活的:對象的所有引用都被釋放,但是對象有可能在finalize()中復活。(3)不可觸及的:對象的finalize()被調用,并且沒有復活,那么就會進入不可觸及狀態。不可觸及的對象不可能被復活,因為每一個對象的finalize()只會被調用一次。以上三種狀態中只有在對象不可觸及時才可以被回收。判定一個對象objA是否可回收,至少要經歷以下兩次標記過程。(1)如果GC Roots到對象objA沒有引用鏈,則進行第一次標記。(2)判斷此對象是否有必要執行finalize()方法。如果對象objA沒有重寫finalize()方法,或者finalize()方法已經被JVM調用過,則JVM視為“沒有必要執行”?,objA被判定為不可觸及。如果對象objA重寫了finalize()方法,且還未執行過,那么objA會被插入到F-Queue隊列中,由一個JVM自動創建的、低優先級的Finalizer線程觸發其finalize()方法執行。finalize()方法是對象逃脫死亡的最后機會,稍后GC會對F-Queue隊列中的對象進行第二次標記。如果objA在finalize()方法中與引用鏈上的任何一個對象建立了聯系,那么在第二次標記時,objA會被移出“即將回收”集合。之后,對象如果再次出現沒有引用存在的情況,finaliz()方法就不會被再次調用,對象會直接變成不可觸及的狀態,也就是說,一個對象的finalize()方法只會被調用一次。