JVM是如何判斷一個對象的存活?

  • 引用計數算法

先來說說我們經常聽到的一種說法,引用計數算法。這種算法的大體思路是:

給每一個對象添加一個引用計數器,每當有一個地方引用對象時,計數器的值+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方法)引用的對象。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章