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