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, 用来标识新空间的地位
* 由更上可知,须要每个对象至多有两个域
* 深度遍历
*/
// 调配
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,吞吐量较高
2 调配速度快,挪动指针,不波及闲暇链表
3 不会产生碎片,复制的行为自身蕴含了压缩
4 深度优先,具备援用关系的对象靠在一起。利于缓存的应用(局部性原理)。
5 堆的利用率低
6 递归的进栈出栈耗费比迭代的模式大,但用迭代的模式是广度优先,jdk 以前是广度,当初都是深度;有进阶算法能够近似靠近深度优先而不须要递归
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;
}
}
}
总结:
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