GC中的對象自救

 

    即便在可達性分析算法中, 被斷定爲不可達的對象, 也並不是是'非死不可' 的, 這時候他們暫處於'緩刑' 階段, 要真正宣告一個對象死亡, 至少要經歷兩次標記過程: java


1. 若是對象失去引用(在進行可達性分析後發現沒有與 GC Roots 相鏈接的引用鏈), 而且該對象的 finalize()方法未調用過, 該對象將會被第一次標記算法


2. 若是這個對象已經被標記了, 那麼這個對象會被放入 ReferenceQueue 隊列中, 由 FinalizeThread 線程去執行, 最終會調用該對象的 finalize() 方法. 這裏所謂的'執行' 並不會保證 finalize() 方法調用結束,爲若是 finalize() 方法執行緩慢, 極端狀況下可能會致使 ReferenceQueue 隊列中其餘的對象永遠處於等待, 甚至致使 GC 系統崩潰.ide

finalize() 方法是對象逃脫死亡命運的最後一次機會, 調用 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 線程來處理

相關文章
相關標籤/搜索