天天看點

JVM如何判斷對象"死"和"活"

     堆中存放着幾乎所有的對象執行個體,垃圾收集器在堆堆進行回收前,首先要确定這些對象哪些還“活着”,哪些已經“死去”。方法有如下兩種:

(1)引用計數法

     算法思想:為對象添加一個引用計數器,每當有一個地方引用該對象時,則該引用計數器值加1,;當引用失效時,則該引用計數器值減1;最後,計數器為0的對象就是不可能再被使用的,也即所謂的“死去”的對象。

    Java中并沒有使用這種算法進行GC,最主要的原因是很難解決對象之間的互相循環引用的問題。如下代碼:

public class TestReferenceCountingGC {
	
	public Object obj = null;
	
	public void testGc(){
		TestReferenceCountingGC obj1 = new TestReferenceCountingGC();
		TestReferenceCountingGC obj2 = new TestReferenceCountingGC();
		obj1.obj = obj2;
		obj2.obj = obj1;
		obj1 = null;
		obj2 = null;
		
		System.gc();
	}
}
           

    在上述代碼中,obj1和obj2互相引用,除此之外,無其他任何引用,但因為互相引用對方導緻引用計數器都不為0,是以無法通知GC收集器回收它們。

(2)根搜尋算法

    Java中是使用根搜尋算法(GC Roots Tracing)判斷對象是否存活的。

    算法思想:選擇名為“GC Roots”的對象為起始點,從這些GC Roots節點開始向下搜尋,搜尋走過的路徑稱之為引用鍊(Reference Chain),當一個對象到GC Roots沒有任何引用鍊時(即不可達,兩者之間無通路),則認為該對象為不可用的。如下圖:

JVM如何判斷對象"死"和"活"

    如上圖,Object3和Object4是互相連接配接的,但和GC Roots無通路,是以Object3和Object4被認為是可回收的對象。

   在Java中,可作為GC Roots的對象有如下:

    a、虛拟機棧(棧幀中的本地變量表)中的引用的對象

    b、方法區中的常量引用的對象

    c、方法區中的類靜态屬性引用的對象

    d、本地方法棧中JNI(Native方法)的引用的對象。

(3)引用的分類

    a、強引用(Strong Reference)

          程式代碼中普遍存在的,比如Object obj = new Object(),隻要存在強引用,GC收集器永遠不會回收被引用的對象。

    b、軟引用(Soft Reference)

          非必須的對象,是否回收,要看目前記憶體情況,如果緊張,則進行回收,否則不回收。當程式抛出記憶體溢出異常時,肯定不存在這種類型的對象。

    c、弱引用(Weak Reference)

          被弱引用關聯的對象隻能生存到下一次GC發生之前。即每次必被回收。

    d、虛引用(Phantom Reference)

          幽靈引用或者幻影引用,一個對象是否有虛引用的存在不會影響到其生存時間,無法通過虛引用擷取對象執行個體。為一個對象設定虛引用關聯的唯一目的是希望能在這個對象被回收時受到一個系統通知。

(4)finalize

     在根搜尋算法中,那些不可達的對象,比如上圖中的Object3和Object4,并非是“真正死亡”的,主要看如下兩次标記過程:

      a、當沒有發現引用鍊時,進行第一次标記,此時進行第一次篩選,條件為此對象是否有必要執行finalize方法。當對象沒有覆寫finalize方法,或者finalize方法已經被JVM調用過,此種情況下認為沒有必要執行finalize方法。

      b、如果有必要執行finalize方法,此時對象會被放置在一個F-Queue隊列中,會有一個優先級比較低的FInalizer線程去執行觸發finalize方法。finalize方法是對象真正判定死活的最後一次機會。此時,GC會對隊列中的對象進行第二次标記,如果對象在finalize方法中完成了自救,即和GC Roots建立了通路,則在第二次标記時該對象将被移出回收的集合。否則,隻能判定對象死了。

     示例:對象自救:

public class TestFinalizeGC {
	public static TestFinalizeGC obj = null;

	@Override
	protected void finalize() throws Throwable {
		super.finalize();
		System.out.println("finalize method executed");
		//完成自救
		TestFinalizeGC.obj = this;
	}
	
	public static void main(String[] args) {
		obj = new TestFinalizeGC();
		obj = null;
		//自救
		System.gc();
	}
}
           

    執行結果是:finalize method executed

     說明對象可以再GC時完成自救,需要注意的是這種自救隻有一次,因為一個對象的finalize方法最多隻會被系統自動調用一次。