即便在可達性分析算法中, 被斷定爲不可達的對象, 也並不是是'非死不可' 的, 這時候他們暫處於'緩刑' 階段, 要真正宣告一個對象死亡, 至少要經歷兩次標記過程: java
1. 若是對象失去引用(在進行可達性分析後發現沒有與 GC Roots 相鏈接的引用鏈), 而且該對象的 finalize()方法未調用過, 該對象將會被第一次標記算法
2. 若是這個對象已經被標記了, 那麼這個對象會被放入 ReferenceQueue 隊列中, 由 FinalizeThread 線程去執行, 最終會調用該對象的 finalize() 方法. 這裏所謂的'執行' 並不會保證 finalize() 方法調用結束,爲若是 finalize() 方法執行緩慢, 極端狀況下可能會致使 ReferenceQueue 隊列中其餘的對象永遠處於等待, 甚至致使 GC 系統崩潰.idefinalize() 方法是對象逃脫死亡命運的最後一次機會, 調用 finalize() 方法後,GC 系統將對 ReferenceQueue 隊列中的對象進行第二次標記, 若是對象要在 finalize() 方法中成功自救, 只要從新與引用鏈創建引用便可, 如:把當前對象( this )賦值給某個類變量或者對象的成員變量, 那麼在第二次標記時它將被移除出 '即將回收' 的集合, 這樣該對象就完成了一次自救; 若是該對象被第二次標記, 那就真的要被回收了. this
摘自周志明老師的<深刻理解JAVA虛擬機>, 稍有改動spa
talk is cheap, show 'papapa' 線程
public class FinalizeEscapeGC { // 用於自救的類變量 private static FinalizeEscapeGC SAVA_HOOK; @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("當前對象: " + this + " 在 " + Thread.currentThread() + " 線程執行 finalize() 方法"); // 把當前對象( this )賦值給某個類變量, 從新與引用鏈創建引用 FinalizeEscapeGC.SAVA_HOOK = this; } public static void main(String[] args) throws Throwable { // 建立一個對象 FinalizeEscapeGC@3654919e, SAVA_HOOK 引用該對象 SAVA_HOOK = new FinalizeEscapeGC(); System.out.println("第一次自救"); SAVA_HOOK = null; // 失去引用 System.gc(); // 運行垃圾回收器 Thread.sleep(100); // 讓 GC 相關線程先走 if (SAVA_HOOK != null) { System.out.println(SAVA_HOOK + " 對象自救成功"); } else { System.out.println("對象已被回收"); } System.out.println("\n第二次自救"); SAVA_HOOK = null; // 失去引用 System.gc(); // 運行垃圾回收器 Thread.sleep(100); // 讓 GC 相關線程先走 if (SAVA_HOOK != null) { System.out.println(SAVA_HOOK + " 對象自救成功"); } else { System.out.println("對象已被回收"); } } }
運行結果:code
第一次自救
當前對象: FinalizeEscapeGC@3654919e 在 Thread[Finalizer,8,system] 線程執行 finalize() 方法
FinalizeEscapeGC@3654919e 對象自救成功對象第二次自救
對象已被回收隊列
另一個值得注意的地方是, 代碼中兩次試圖實現自救,虛擬機
運行結果倒是: 一次自救成功, 一次失敗
這是由於任何對象的 finalize() 方法都只會被調用一次, 若是對象面臨下一次 GC, 它的 finalize() 方法不會被再次執行, 所以第二次自救失敗了.
Finalizer 線程, 其優先級爲 8, 用於在 GC 前, 執行對象的 finalize() 方法
關於 Finalizer 線程:
JVM 在 GC 時會將失去引用的對象包裝成 java.lang.ref.Finalizer 對象(java.lang.ref.Reference 抽象類的實現),並放入ReferenceQueue,由 Finalizer$FinalizeThread 線程來處理