可達性演算法
在 Java 中,是通過可達性分析來判斷物件是否存活的。該演算法的基本思路是以一系列名為GC Roots
的物件作為起始點,從這些節點開始向下搜尋,搜尋所走過的路徑稱為引用鏈Reference Chain
,當一個物件到GC Roots
沒有任何引用鏈相連時(用圖論的話來說就是GC Roots
到這個物件不可達),則證明此物件時不可用的。
能成為GC Roots
的物件包括下面幾種:
- 棧幀中的區域性變量表中的引用的物件。
- 方法區中的類靜態屬性引用的物件。
- 方法區中的常量引用的物件。
- Native 方法引用的物件。
【為何不用引用計數法?】
引用計數演算法:每當一個引用指向該物件,計數器的值就 +1,引用失效計數器的值就 -1。
引用計數演算法很難解決迴圈引用的問題。迴圈引用舉例:
public class ReferenceCountingGC { public Object instance = null; public static void testGC() { ReferenceCountingGC objA = new ReferenceCountingGC(); ReferenceCountingGC objB = new ReferenceCountingGC(); objA.instance = objB; objB.instance = objA; objA = null; objB = null; } }
上述程式碼中,到最後雖然 objA 和 objB 都為 null,但是兩個物件的 instance 引用還都指向對方,導致這兩個物件的引用計數器的值都為 1,無法回收。
【不可達物件的回收過程】
首先進行第一次標記,標記所有的不可達物件,在下次 GC 之前會執行物件的 finalize 方法。
- 沒有重寫 finalize 方法或 finalize 方法已經被虛擬機器呼叫過的物件被直接清除。
- 重寫 finalize 方法並且尚未呼叫的物件,將被放進一個佇列中,稍後觸發 finalize 方法。
finalize 方法是最後一個逃生門,執行過程中如果重新變的可達,就會在第二次標記時移出佇列,第二次標記之後,佇列中的物件都會被回收。