Concurrent mark sweep GC
很多应用对响应时间的要求要大于吞吐量。
YGC并不暂停多少时间,但FGC对时间的暂用还是很长的。特别是在年老区使用的空间较多时。
因此, HotSpot引入了一个叫做CMS的收集器,也叫低延时收集器。
CMS的YGC
与并行GC同样的方式: stop-the-world 加上 copy。
CMS的FGC
CMS的FGC在大部分是和应用程序一起并发的!
CMS在FGC的时候,一开始需要做一个短暂的暂停,这个阶段称为最初标记:识别所有被引用的对象。
在并发标记时候,会和应用程序一起运行。
因为并发标记是和程序一起运行的,所以在并发标记结束的时候,不能保证所有被引用的对象都被标记,
为了解决这个问题,GC又进行了一次暂停,这个阶段称为:重标识(remark)。
在这个过程中,GC会重新对在并发标阶段时候有修改的对象做标记。
因为remark的暂停要大于最初标记,所以在这时候,需要使用多线程来并行标记。
在上述动作完成之后,就可以保证所有被引用的对象都被标记了。
因此,并发清理阶段就可以并发的收集垃圾了。
下图是serial gc 和 CMS gc 的对比:
因为要增加很多额外的动作,比如对被引用的对象重新标记,增加了CMS的工作量,所以他的GC负荷也相应的增加。
CMS是唯一没有进行压缩的GC。如下图:
没有压缩,对于GC的过程,是节约了时间。但因此产生了内存碎片,所以对于新对象在年老区的分配,就产生了速度上的影响,当然,也就包括了对YGC时间的影响了(因为有可能一些对象晋升到old区中)。
CMS的另一个缺点,就是他需要的堆比较大,因为在并发标记的时候和并发清除的时候,应用程序很有可能在不断产生新的对象,而垃圾又还没有被删除。
另外,在最初标记之后的并发标记时,原先被引用的对象,有可能变成垃圾。但在这一次的GC中,这是没有被删除的。这种垃圾叫做:漂流垃圾。
最后,由于没有进行压缩,由此而带来了内存碎片。
为了解决这个问题,CMS对热点object大小进行了统计,并且估算之后的需求,然后把空闲的内存进行拆分或者合并来满足后续的需求。
与其他的GC不同,CMS并不在年老区满了之后才开始GC,他需要提前进行GC,用以满足在GC同时需要额外的内存。
如果在GC的同时,内存不能满足要求了,则GC就变成了并行GC或者串行GC。
为了防止这种情况,会根据上一次GC的统计来确定启动时间。
或者是当年老区超过初始容量的话,CMS GC就会启动。
初始容量的设置可以在JVM启动时增加参数: -XX:CMSInitiatingOccupancyFraction=n
n是一个百分比,默认值为68。
总之,CMS比并行GC花费了更少的暂停时间,但是牺牲了吞吐量,以及需要更大的堆区。
额外模式
为了防止在并发标记的时候,GC线程长期占用CPU,CMS可以把并发标记的时候停下来,把cpu让给应用程序。
收集器会想并发标记分解成很小的时间串任务,在YGC之间来执行。
这个功能对于机器的CPU个数少,但又想降低暂停时间的应用来说,非常有用。
何时使用CMS
当CPU资源较空闲,并且需要很低的暂停时间时,可以选择CMS。比如 web servers。
选择CMS
选择CMS GC: 增加参数 -XX:UseConcMarkSweepGC
开启额外模式: 增加参数 -XX:+CMSIncreamentalMode