4、垃圾收集器
一、Serial收集器(-XX:+UseSerialGC(年轻代) -XX:+UseSerialOldGC(老年代))
1.1、年轻代采用复制算法,老年代采用标记-整理算法
二、Parallel Scavenge收集器(-XX:+UseParallelGC(年轻代),-XX:+UseParallelOldGC(老年代))
2.1、新生代采用复制算法,老年代采用标记-整理算法
三、ParNew收集器(-XX:+UseParNewGC)
3.1、新生代采用复制算法,老年代采用标记-整理算法
四、CMS收集器(-XX:+UseConcMarkSweepGC(old))
初始标记: 暂停所有的其他线程(STW),并记录下GC roots直接能引用的对象,速度很快。
并发标记: 并发标记阶段就是从GC Roots的直接关联对象开始遍历整个对象图的过程, 这个过程耗时较长但是不需要停顿用户线程, 可以与垃圾收集线程一起并发运行。因为用户程序继续运行,可能会有导致已经标记过的对象状态发生改变。
重新标记: 重新标记阶段就是为了修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短。主要用到三色标记里的增量更新算法做重新标记。
并发清理: 开启用户线程,同时GC线程开始对未标记的区域做清扫。这个阶段如果有新增对象会被标记为黑色不做任何处理。
并发重置:重置本次GC过程中的标记数据。
4.1、优缺点
对CPU资源敏感(会和服务抢资源)。
无法处理浮动垃圾(在并发标记和并发清理阶段又产生垃圾,这种浮动垃圾只能等到下一次gc再清理了)。
它使用的回收算法-“标记-清除”算法会导致收集结束时会有大量空间碎片产生(当然通过参数-XX:+UseCMSCompactAtFullCollection可以让jvm在执行完标记清除后再做整理).。
执行过程中的不确定性,会存在上一次垃圾回收还没执行完,然后垃圾回收又被触发的情况,特别是在并发标记和并发清理阶段会出现,一边回收,系统一边运行,也许没回收完就再次触发full gc,也就是"concurrent mode failure",此时会进入stop the world,用serial old垃圾收集器来回收。
浮动垃圾
4.2、相关核心参数
-XX:+UseConcMarkSweepGC:启用cms。
-XX:ConcGCThreads:并发的GC线程数。
-XX:+UseCMSCompactAtFullCollection:Full GC之后做压缩整理(减少碎片)。
-XX:CMSFullGCsBeforeCompaction:多少次Full GC之后压缩一次,默认是0,代表每次Full GC后都会压缩一次。
-XX:CMSInitiatingOccupancyFraction: 当老年代使用达到该比例时会触发Full GC(默认是92,这是百分比)。
-XX:+UseCMSInitiatingOccupancyOnly:只使用设定的回收阈值(-XX:CMSInitiatingOccupancyFraction设定的值),如果不指定,JVM仅在第一次使用设定值,后续则会自动调整。
-XX:+CMSScavengeBeforeRemark:在CMS GC前启动一次minor gc,目的在于减少老年代对年轻代的引用,降低CMS GC的标记阶段时的开销,一般CMS的GC耗时 80%都在标记阶段。
-XX:+CMSParallellnitialMarkEnabled:表示在初始标记的时候多线程执行,缩短STW。
-XX:+CMSParallelRemarkEnabled:在重新标记的时候多线程执行,缩短STW。
五、垃圾收集底层算法实现-三色标记
黑色: 表示对象已经被垃圾收集器访问过, 且这个对象的所有引用都已经扫描过。 黑色的对象代表已经扫描过, 它是安全存活的, 如果有其他对象引用指向了黑色对象, 无须重新扫描一遍。 黑色对象不可能直接(不经过灰色对象) 指向某个白色对象。
灰色: 表示对象已经被垃圾收集器访问过, 但这个对象上至少存在一个引用还没有被扫描过。
白色: 表示对象尚未被垃圾收集器访问过。 显然在可达性分析刚刚开始的阶段, 所有的对象都是白色的, 若在分析结束的阶段, 仍然是白色的对象, 即代表不可达(要被清除的)。
5.1、漏标
var G = objE.fieldG; objE.fieldG = null; // 灰色E 断开引用 白色G objD.fieldG = G; // 黑色D 引用 白色G
var G = objE.fieldG; // 1.读 objE.fieldG = null; // 2.写 objD.fieldG = G; // 3.写
增量更新(Incremental Update)
原始快照(Snapshot At The Beginning,SATB)
5.2、写屏障
/**
* @param field 某对象的成员变量,如 a.b.d
* @param new_value 新值,如 null
*/
void oop_field_store(oop* field, oop new_value) {
// 赋值操作
*field = new_value;
}所谓的写屏障,其实就是指在赋值操作前后,加入一些处理:
void oop_field_store(oop* field, oop new_value) {
// 写屏障-写前操作
pre_write_barrier(field);
*field = new_value;
// 写屏障-写后操作
post_write_barrier(field, value);
}写屏障实现原始快照(SATB)
void pre_write_barrier(oop* field) {
// 获取旧值
oop old_value = *field;
// 记录原来的引用对象
remark_set.add(old_value);
}写屏障实现增量更新
void post_write_barrier(oop* field, oop new_value) {
if($gc_phase == GC_CONCURRENT_MARK && !isMarkd(field)) {
// 记录新引用的对象
remark_set.add(new_value);
}
}5.3、读屏障
oop oop_field_load(oop* field) {
// 读屏障-读取前操作
pre_load_barrier(field);
return *field;
}读屏障是直接针对第一步:var G = objE.fieldG;,当读取成员变量时,一律记录下来
void pre_load_barrier(oop* field) {
oop old_value = *field;
// 记录读取到的对象
remark_set.add(old_value);
}六、记忆集与卡表
版权声明
非特殊说明,本文由Zender原创或收集发布,欢迎转载。
ZENDER






发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。