CMS垃圾收集器&三色标记-JVM(十二)

2023-09-05 14:34:35 浏览数 (2)

上篇文章说了CMS垃圾收集器是赋值清除,所以他不可以碎片整理,于是jvm支持两个参数,几次fullGC之后碎片整理压缩空间。Cms他会抢占cpu资源,因为是并行运行,所以会有浮动垃圾。还有执行不确定性,垃圾收集完,继续进入新的对象,导致异常concurrent mode faliture,最后用serial old处理,可以用jvm的fraction参数来参数百分之多少的时候需要GC,这样就预留充足的空间存储新对象。

垃圾收集器CMS-JVM(十一)

一、实际场景

前面介绍了cms的参数,那么我们如何应用呢?

前面文章我们介绍了parallel作用于年轻代和老年代回收,当时场景是因为回收的对象比较大,不能进入survivor,导致直接从eden进入old,这时候eden满的那一瞬间,也就是minor GC时候最后一个对象不会在年轻代回收,进入老年代,当时我们通过设置年轻代大小的参数,来解决了减少fullGC的问题。

那我们现在既然学习了cms和parNew,就试着用新的方法来解决。

前面我们的参数jvm参数配置是:

Java -Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:MetaspaceSize=256M

-XX:MaxMetaspaceSize=256M -XX SurvivorRatio=8

这样配合当时的应用场景绝对不发生fullGc是不可能的。当时我们的场景是每秒产生60MB,但如果峰值增加,在23秒,24秒的时候,每次cpu分配给单个线程的运行已经超过几秒,这时候minor GC的时候会超过60M的数据移动到survivor,这时候200M的S0已经未必放的下这些存活的数据,需要移动到old,这时候当old满的时候又会触发fullGC。

但这种秒杀场景,即使出现这种情况影响也不大,因为当old满的时候,秒杀的前10分钟已经过去,这时候发生一次fullGC也不影响。

Java -Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:MetaspaceSize=256M

-XX:MaxMetaspaceSize=256M -XX SurvivorRatio=8

-XX:MaxTenuringThreshold=5 -XX:PretenureSizeThreshold=1M

-XX:UseParNewGC -XX: UseConcMarkSweepGC

-XX:UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=3

这个参数一次的fullGC不会存在大量参数,所以一次未必需要整理,配置两到三次都没有问题。

二、三色标记

这个底层并不是java实现,而是c 实现的。

前面说了在gc发生的时候,用户线程和垃圾回收线程并行运行,对于多标和漏标的情况可能会发生。

多标影响还好,可以在下一次gc的时候清除,在并发标记的时候,就会存在多标的现象,但是少标或者漏标影响比较大。

三色标记指GCroots 可达性分析遍历对象过程中遇到的对象,按照是否访问过标记为三种颜色。

黑色:表示对象已经被垃圾收集器访问过,且这个对象所有引用都扫描过,它是存活的对象。如果其他对象引用指向黑色,无须重新扫描。黑色对象不可以直接(不经过灰色对象)指向某个白色对象。

灰色:表示对象已经被垃圾回收访问过,但至少还有一个或者以上引用没被扫描到。

白色:表示对象未被垃圾回收器访问过。

前面说的多标浮动垃圾对象,三色标记的处理办法则是直接标记成黑色,本轮GC不会清除,但是下一轮则可能部分对象变为垃圾对象。

漏标-读写屏障

漏标则会把未标记的对象无删除,这种验证bug则会有两种办法解决,增量更新和原始快照。

漏标会找到之前的引用,重新去扫描。

增量更新指一旦有新插入的指向白色,则会变成灰色对象。

原始快照则是让对象在本轮gc存活,在下一次gc再清理,可以理解为浮动垃圾。

这些都是在写屏障实现的。

三、记忆集与卡表

新生代做gcRoots 的时候可能遇到跨代应用场景,这时候总不能再去old代理扫描一遍。

于是新生代里有一个记录集(remember set)数据结构,记录这种跨代应用的对象,避免GC roots去老年代扫描。事实上,老年代也有这种问题,G1和ZGC收集器都有这种问题。

hotSpot使用叫做卡表Cardtable的方式实现这种收集,也是目前最常用的一种方式。

0 人点赞