ParallelScavenge + Parallel Old

ParalletScavenge:複製算法,多線程,關注吞吐率

Copying GC(GC複製算法):算法

最先是Robert R.Fenichel和Jerome C.Yochelson提出,簡單說將空間分爲From和To,當From空間徹底佔滿時,gc將活動對象所有複製到To空間,複製完成後,From和To的使命互換。緩存

爲了保證From中全部活着的對象都能複製到To裏,要求From和To空間大小一致。數據結構

coping(){
  free = to_start;
  for(r : roots){
    *r = copy(*r)
  }
  swap(from_start,to_start)
}
/**
*free設在To空間的開頭
*第四行copy函數,複製能從根引用到的對象及其子對象,返回*r所在的新空間。
*而後交換From和To,對From空間進行gc
*/
 
copy(obj){
  if(obj.tag != COPIED){
    copy_data(free,obj,obj.size);
    obj.tag = COPIED;
    obj.forwarding = free;
    free = obj.size
 
    for(child : children(obj.forwarding){
      *child = copy(*child)
    }
  }
  return obj.forwarding
}
/**
*obj.tag 並非單獨的一個域,只是佔用了obj的field1,一旦obj被複制完畢,其在From空間的域值更改沒有關係
*obj.forwarding也不是一個單獨的域,佔用了obj.field2,用來標識新空間的位置
* 由更上可知,須要每一個對象至少有兩個域
* 深度遍歷
*/

image.png

//分配
new_obj(size){
  if(free + size > from_start + HEAP_SIZE /2){
    copying(); //空間不足,gc
    if(free + size > from_start + HEAP /2){
       allocation_fail();//仍是不夠,分配失敗
    }
  }
  obj = free;
  obj.size = size;
  free += size;
  return obj;
}

copying算法的總結:多線程

1 copying算法的時間複雜度只跟活躍對象的個數有關,能夠在較短期內完成gc,吞吐量較高jvm

2 分配速度快,移動指針,不涉及空閒鏈表函數

3 不會產生碎片,複製的行爲自己包含了壓縮spa

4 深度優先,具備引用關係的對象靠在一塊兒。利於緩存的使用(局部性原理)。線程

5 堆的利用率低指針

6  遞歸的進棧出棧消耗比迭代的形式大,但用迭代的形式是廣度優先,jdk之前是廣度,如今都是深度;有進階算法能夠近似接近深度優先而不須要遞歸code

 Parallel Old:標記-整理,多線程

Mark Compact GC 標記-壓縮算法:

首先看Donald E.Knuth研究出來的Lisp2算法:

compaction_phase(){
  set_forwarding_ptr();
  adjust_ptr();
  move_obj();
}
 
set_forwarding_ptr(){
  scan = new_address = heap_start;
  while(scan < heap_end){  //第一次搜索堆
    if(scan.mark == TRUE){  //被標記爲活對象
      scan.forwarding = new_address;//new_address指向移動後的地址
      new_address += scan.size;
    }
    scan += scan.size; //掃描下一個活對象
  }
}
 
adjust_ptr(){
  for(r : roots){
    *r = (*r).forwarding;
  }
  
  scan = heap_start;
  while(scan < heap_end){  //第二次搜索堆
    if(scan.mark = TRUE){
      for(child : children(scan))
        *child = (*child).forwarding
    }
    scan += scan.size;
  }
}
 
move_obj(){
  scan = free = heap_start;
  while(scan < heap_end){//第三次搜索堆
     if(scan.mark = TRUE){
        new_address = scan.forwarding;
        copy_data(new_address,scan,scan.size);
        new_address.forwarding = null;
        new_address.mark = FALSE:
        free += new_address.size;
        scan += scan.size;
     }
  }
}

image.png

總結:

1 堆利用率高

2 壓縮空間,沒有碎片

3 時間消耗大:須要搜索三次堆,且時間與堆大小成正比

還有一個Two-Finger算法,能夠作到只搜索兩次,但要求全部對象整理成大小一致,這個約束比較苛刻,jvm用的應該是Lisp2。

分代垃圾回收:

可行性來源於經驗:「大部分的對象在生成後很快就變成了垃圾,不多有對象能活得好久」

有些GC算法的時間是和活着的對象的個數成正比,好比標記-清除MarkSweep,複製Copying;

minorGC:新生代GC,minor指小規模的;

majorGC:老年代GC

promotion:新生代對象通過若干次gc仍活着的晉升爲老年代。

Ungar的分代垃圾回收:David Ungar研究出來的將copying算法與分代思想結合

 1  堆劃分爲4個空間:生成空間new_start,2個大小相等的倖存空間survivor1_start,survivor2_start,老年代空間old_start

    2個倖存空間相似於copying gc中的From和To,每次利用生成空間+1個倖存空間,gc後活着的對象複製到另外一個倖存空間

2  考慮:新生代gc:須要考慮老年代空間對新生代空間中對象的引用

Remembered Set & Card Table

image.png
Remembered Set:實現部分垃圾收集時(partial gc),用於記錄從非收集部分指向收集部分的指針的集合的 抽象數據結構了。

在分代垃圾回收裏,Remembered Set 用來記錄老年代對象 對  新生代對象 的跨代引用;在regional collector中,Remembered Set 用來記錄跨region的指針。

粒度;數據結構;寫屏障

3 對象的晉升

 MaxTenuringThreshold 定義了晉升老年代的對象的最大年齡,大於這個年齡,必定會晉升,小於這個年齡,也有可能晉升,取決於TargetSurvivorRatio。

當年齡爲age的對象的大小之和超過了targetSurvivorratio * survivor,則實際用來判斷是否晉升的年齡是這個動態的age,不是maxtenuringthreshold

相關文章
相關標籤/搜索