1. 程式人生 > >java垃圾回收演算法之-標記清除

java垃圾回收演算法之-標記清除

java垃圾回收演算法之-引用計數器,這個演算法其中一個優點便是,實時性,只要物件的引用計數器的值為0,則立刻回收。接下來介紹的標記清除演算法,當物件的引用計數器的值為0時,不會立刻被回收的

概念介紹

root物件

在標記清除演算法中,會把如下物件稱之為root物件

  1. 被棧中的變數(棧中存的是物件的引用)所引用的物件
  2. 被static變數引用的物件

可訪問的物件

如果棧中有一個變數a引用了一個物件,那麼該物件是可訪問的,如果該物件中的某一個欄位引用了另一個物件b,那麼b也是可訪問的。可訪問的物件也稱之為live物件

標記清除演算法介紹

該演算法有兩個階段。
1. 標記階段:找到所有可訪問的物件,做個標記
2. 清除階段:遍歷堆,把未被標記的物件回收

備註:

該演算法一般應用於老年代,因為老年代的物件生命週期比較長。

標記階段演算法

虛擬碼類似如下:

for each root variable r
    mark (r);
sweep ();

為了能夠區分物件是live的,可以為每個物件新增一個marked欄位,該欄位在物件建立的時候,預設值是false
假設有一個物件p,p物件還間接的引用了其他物件,那麼可以使用一個遞迴演算法去進行標記,例如:

void mark(Object p)
    if (!p.marked)
        p.marked = true;
        for each
Object q referenced by p mark (q);

這個mark方法只有當所有物件已經被mark後才會退出。

清除階段演算法

在這個階段,需要去遍歷堆中所有物件,並找出未被mark的物件,進行回收。與此同時,那些被mark過的物件的marked欄位的值會被重新設定為false,以便下次的垃圾回收。
虛擬碼如下:

void sweep ()
    for each Object p in the heap
        if (p.marked)
            p.marked = false
        else
heap.release (p);

下面用一張圖來表示標記清除演算法的整個過程。
這裡寫圖片描述

標記清除演算法的優點和缺點
1. 優點
- 是可以解決迴圈引用的問題
- 必要時才回收(記憶體不足時)
-
2. 缺點:
- 回收時,應用需要掛起,也就是stop the world。
- 標記和清除的效率不高,尤其是要掃描的物件比較多的時候
- 會造成記憶體碎片(會導致明明有記憶體空間,但是由於不連續,申請稍微大一些的物件無法做到),如下圖:
這裡寫圖片描述

解決迴圈引用

出現迴圈引用的程式碼如下:

class TestA{
  public TestB b;

}
class TestB{
  public TestA a;
}
public class Main{
    public static void main(String[] args){
        A a = new A();
        B b = new B();
        a.b=b;
        b.a=a;
        a = null;
        b = null;
    }
}

對應的圖如下:
這裡寫圖片描述

這個時候,當a = null; b = null;的時候,影象變成如下:

這裡寫圖片描述

那麼使用標記清除演算法是可以回收a和b的,原因是標記清除演算法是從棧中根物件開始的,改演算法走完後,a物件和b物件是沒有被標記的,會被直接回收。