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,用來標識新空間的位置 * 由更上可知,須要每一個對象至少有兩個域 * 深度遍歷 */
//分配 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
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; } } }
總結:
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
Remembered Set:實現部分垃圾收集時(partial gc),用於記錄從非收集部分指向收集部分的指針的集合的 抽象數據結構了。
在分代垃圾回收裏,Remembered Set 用來記錄老年代對象 對 新生代對象 的跨代引用;在regional collector中,Remembered Set 用來記錄跨region的指針。
粒度;數據結構;寫屏障
3 對象的晉升
MaxTenuringThreshold 定義了晉升老年代的對象的最大年齡,大於這個年齡,必定會晉升,小於這個年齡,也有可能晉升,取決於TargetSurvivorRatio。
當年齡爲age的對象的大小之和超過了targetSurvivorratio * survivor,則實際用來判斷是否晉升的年齡是這個動態的age,不是maxtenuringthreshold