- 引用計數算法
先來說說我們經常聽到的一種說法,引用計數算法。這種算法的大體思路是:
給每一個對象添加一個引用計數器,每當有一個地方引用對象時,計數器的值+1;引用失效,值-1;任何時刻當計數器值爲0時,這對象就是不可能再被使用的。
客觀的講,引用計數算法實現簡單,效率高。然而在主流的Java虛擬機中有沒有使用此算法呢?我們一起來看看下面這個例子:
public class CountGC {
public Object instance = null;
private static final int _1MB = 1024*1024;
private byte[] bigSize = new byte[2 * _1MB];
public static void main(String[] args) {
testGC();
}
public static void testGC() {
CountGC objA = new CountGC();
CountGC objB = new CountGC();
objA.instance = objB;
objB.instance = objA;
objA = null;
objB = null;
System.gc();//如果在這裏發生GC,objA和objB是否被回收
}
}
運行結果如下圖:
可以看出:代碼中的 testGC() 方法,對象 objA 和 objB 都有字段 instance ,賦值令objA.instance = objB; objB.instance = objA;
除此之外,這兩個對象再沒有其他任何引用。在引用賦值爲 null 後,這兩個對象已經不可能再被訪問,但因爲互相引用對方,導致他們的計數器值都不爲0.
但是我們在GC日誌中發現“7M->0M”,意味着虛擬機回收了他們。所以Java虛擬機並沒有使用引用計數算法。這種算法的最大缺點是:很難解決對象之間相互循環引用的問題。
那麼在Java主流虛擬機中使用的是什麼算法呢?答案是“可達性分析算法”!
- 可達性分析算法
如下圖,本算法會設定一系列的成爲“ GC Roots ”的對象作爲起始點,從這些節點往下搜索,搜索走過的路徑成爲引用鏈。
當一個對象到GC Roots沒有任何引用鏈相連接(對象到GC Roots 不可達),證明此對象是不可用的。
在Java中,可以作爲GC Roots的對象:
1. 虛擬機棧(棧幀中的本地變量表)中引用的對象;
2. 方法區中靜態屬性引用的對象;
3. 方法區中常量引用的對象;
4. 本地方法棧中JNI(Native方法)引用的對象。