CMS GC

 -server -Xms8192M -Xmx8192M -Xmn3072M -XX:ParallelGCThreads=20 -XX:SurvivorRatio=6 -XX:TargetSurvivorRatio=80 -XX:InitialTenuringThreshold=15 -XX:MaxTenuringThreshold=15 -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+DisableExplicitGC -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=50 -XX:+ScavengeBeforeFullGC -XX:-UseAdaptiveSizePolicy -XX:+CMSScavengeBeforeRemark


SurvivorRatio 为将幸存空间比率设置为1:8,导致更大的生存空间(比率越小,空间越大)。 更大的幸存者空间使得短暂的物体在年轻一代中死亡的时间更长

简单计算方式为-Xmn/SurvivorRatio+2

即为3072/6+2 = 384M 

-XX:TargetSurvivorRatio=80

允许占用90%的幸存者空间,而不是默认的50%,从而更好地利用幸存者的空间记忆。

为什么不是100%?

可以看到在每次minor GC后,会对这个存活周期的阈值做计算,计算的代码如下:
size_t desired_survivor_size = (size_t)((((double) survivor_capacity)*TargetSurvivorRatio)/100);
size_t total = 0;
int age = 1;
assert(sizes[0] == 0, "no objects with age zero should be recorded");
while (age total += sizes[age];
// check if including objects of age 'age' made us pass the desired
// size, if so 'age' is the new threshold
if (total > desired_survivor_size) break;
age++;
}
int result = age
其中survivor_capacity 是一个survivor的大小,从上面的代码可看出,在计算存活周期这个阈值时,hotspot会遍历所有age的table(注意从age=1的开始,也就意味着查找在servivor区的对象),并对其所占用的大小进行累积,当累积的大小超过了survivor space的一半时,则以这个age作为新的存活周期阈值,最后取age和MaxTenuringThreshold中更小的一个值作为threshold 的值。下次再Minor gc时候,age>=threshold 的对象,将进入old gen区。

这个规则对于Serial GC以及ParNew GC(但对于开启了UseAdaptiveSizePolicy的ParNew GC而言也无效,默认是不开启的)均有效,对于PS(Parallel Scavenge) GC而言,在默认的情况下第一次以InitialTenuringThreshold(默认为7)为准,之后在每次minor GC后均会动态计算,规则比上面的复杂,后续再另写一篇blog来说,在设置-XX:-UseAdaptiveSizePolicy后(注意是减号),则以MaxTenuringThrehsold为准,并且不会重新计算,会是恒定值。


-XX:+UseCMSInitiatingOccupancyOnly 

我们用-XX+UseCMSInitiatingOccupancyOnly标志来命令JVM不基于运行时收集的数据来启动CMS垃圾收集周期。而是,当该标志被开启时,JVM通过CMSInitiatingOccupancyFraction的值进行每一次CMS收集,而不仅仅是第一次。然而,请记住大多数情况下,JVM比我们自己能作出更好的垃圾收集决策。因此,只有当我们充足的理由(比如测试)并且对应用程序产生的对象的生命周期有深刻的认知时,才应该使用该标志。

-XX:CMSInitiatingOccupancyFraction=50

当堆满之后,并行收集器便开始进行垃圾收集,例如,当没有足够的空间来容纳新分配或提升的对象。对于CMS收集器,长时间等待是不可取的,因为在并发垃圾收集期间应用持续在运行(并且分配对象)。因此,为了在应用程序使用完内存之前完成垃圾收集周期,CMS收集器要比并行收集器更先启动。

因为不同的应用会有不同对象分配模式,JVM会收集实际的对象分配(和释放)的运行时数据,并且分析这些数据,来决定什么时候启动一次CMS垃圾收集周期。为了引导这一过程, JVM会在一开始执行CMS周期前作一些线索查找。该线索由 -XX:CMSInitiatingOccupancyFraction=<value>来设置,该值代表老年代堆空间的使用率。比如,value=75意味着第一次CMS垃圾收集会在老年代被占用75%时被触发。通常CMSInitiatingOccupancyFraction的默认值为68(之前很长时间的经历来决定的)。


-XX:+CMSParallelRemarkEnabled

如果remark还是过长的话,可以开启-XX:+CMSScavengeBeforeRemark选项,强制remark之前开始一次minor gc,减少remark的暂停时间,但是在remark之后也将立即开始又一次minor gc。

-XX:+ScavengeBeforeFullGC

这个flag,则会在Full GC之前对年轻代进行一次Minor GC;其他情况根本不需要Full GC之前先执行Minor GC,Full GC会对年轻代发起GC。

-XX:+CMSScavengeBeforeRemark

这个flag,这给了CMS 在Major GC时触发Minor GC的机会,但这个Flag默认是false




2018-01-01T05:58:53.155-0800:199365.969: [CMS-concurrent-mark-start]

2018-01-01T05:58:59.847-0800: 199372.661: [GC(Allocation Failure) 2018-01-01T05:58:59.847-0800: 199372.661: [ParNew:1677824K->1677824K(1887488K), 0.0000663 secs]2018-01-01T05:58:59.847-0800:199372.661: [CMS2018-01-01T05:59:54.688-0800: 199427.502: [CMS-concurrent-mark:61.407/61.533 secs] [Times: user=315.03 sys=0.57, real=61.53 secs]

(concurrentmode failure):6247341K->6248663K(6291456K), 97.7790252 secs]7925165K->6248663K(8178944K), [Metaspace: 72167K->72167K(1124352K)],97.7798620 secs] [Times: user=316.44 sys=0.59, real=97.78 secs]

2018-01-01T06:00:39.672-0800: 199472.486: [GC(CMS Initial Mark) [1 CMS-initial-mark: 6248663K(6291456K)] 6558038K(8178944K),0.0760592 secs] [Times: user=1.06 sys=0.00, real=0.07 secs]

2018-01-01T06:00:39.751-0800: 199472.565:[CMS-concurrent-mark-start]

2018-01-01T06:00:47.427-0800: 199480.241: [GC(Allocation Failure) 2018-01-01T06:00:47.427-0800: 199480.241: [ParNew:1677824K->1677824K(1887488K), 0.0000603 secs]2018-01-01T06:00:47.427-0800:199480.241: [CMS2018-01-01T06:01:51.546-0800: 199544.360: [CMS-concurrent-mark:71.627/71.795 secs] [Times: user=368.21 sys=0.79, real=71.79 secs]

(concurrent mode failure): 6248663K->6248453K(6291456K), 107.7267830 secs]7926487K->6248453K(8178944K), [Metaspace: 72167K->72167K(1124352K)],107.7277540 secs] [Times: user=364.20 sys=0.56, real=107.73 secs]

2018-01-01T06:02:37.181-0800: 199589.995: [GC(CMS Initial Mark) [1 CMS-initial-mark: 6248453K(6291456K)] 6614088K(8178944K),0.1051487 secs] [Times: user=1.21 sys=0.14, real=0.10 secs]

2018-01-01T06:02:37.288-0800: 199590.102:[CMS-concurrent-mark-start]

2018-01-01T06:02:43.221-0800: 199596.035: [GC(Allocation Failure) 2018-01-01T06:02:43.221-0800: 199596.035: [ParNew:1677824K->1677824K(1887488K), 0.0000785 secs]2018-01-01T06:02:43.221-0800:199596.035: [CMS2018-01-01T06:03:14.399-0800: 199627.213: [CMS-concurrent-mark:37.043/37.112 secs] [Times: user=196.38 sys=1.07, real=37.12 secs]



CMS是一款优秀的收集器,它的最主要优点在名字上已经体现出来了:并发收集、低停顿,Sun的一些官方文档里面也称之为并发低停顿收集器(Concurrent Low Pause Collector)。但是CMS还远达不到完美的程度,它有以下三个显著的缺点:
  • CMS收集器对CPU资源非常敏感。其实,面向并发设计的程序都对CPU资源比较敏感。在并发阶段,它虽然不会导致用户线程停顿,但是会因为占用了一部分线程(或者说CPU资源)而导致应用程序变慢,总吞吐量会降低。CMS默认启动的回收线程数是(CPU数量+3)/ 4,也就是当CPU在4个以上时,并发回收时垃圾收集线程最多占用不超过25%的CPU资源。但是当CPU不足4个时(譬如2个),那么CMS对用户程序的影响就可能变得很大,如果CPU负载本来就比较大的时候,还分出一半的运算能力去执行收集器线程,就可能导致用户程序的执行速度忽然降低了50%,这也很让人受不了。为了解决这种情况,虚拟机提供了一种称为“增量式并发收集器”(Incremental Concurrent Mark Sweep / i-CMS)的CMS收集器变种,所做的事情和单CPU年代PC机操作系统使用抢占式来模拟多任务机制的思想一样,就是在并发标记和并发清理的时候让GC线程、用户线程交替运行,尽量减少GC线程的独占资源的时间,这样整个垃圾收集的过程会更长,但对用户程序的影响就会显得少一些,速度下降也就没有那么明显,但是目前版本中,i-CMS已经被声明为“deprecated”,即不再提倡用户使用。
  • CMS收集器无法处理浮动垃圾(Floating Garbage),可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生。由于CMS并发清理阶段用户线程还在运行着,伴随程序的运行自然还会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS无法在本次收集中处理掉它们,只好留待下一次GC时再将其清理掉。这一部分垃圾就称为“浮动垃圾”。也是由于在垃圾收集阶段用户线程还需要运行,即还需要预留足够的内存空间给用户线程使用,因此CMS收集器不能像其他收集器那样等到老年代几乎完全被填满了再进行收集,需要预留一部分空间提供并发收集时的程序运作使用。在默认设置下,CMS收集器在老年代使用了68%的空间后就会被激活,这是一个偏保守的设置,如果在应用中老年代增长不是太快,可以适当调高参数-XX:CMSInitiatingOccupancyFraction的值来提高触发百分比,以便降低内存回收次数以获取更好的性能。要是CMS运行期间预留的内存无法满足程序需要,就会出现一次“Concurrent Mode Failure”失败,这时候虚拟机将启动后备预案:临时启用Serial Old收集器来重新进行老年代的垃圾收集,这样停顿时间就很长了。所以说参数-XX:CMSInitiatingOccupancyFraction设置得太高将会很容易导致大量“Concurrent Mode Failure”失败,性能反而降低。
  • 还有最后一个缺点,在本节在开头说过,CMS是一款基于“标记-清除”算法实现的收集器,如果读者对前面这种算法介绍还有印象的话,就可能想到这意味着收集结束时会产生大量空间碎片。空间碎片过多时,将会给大对象分配带来很大的麻烦,往往会出现老年代还有很大的空间剩余,但是无法找到足够大的连续空间来分配当前对象,不得不提前触发一次Full GC。为了解决这个问题,CMS收集器提供了一个-XX:+UseCMSCompactAtFullCollection开关参数,用于在“享受”完Full GC服务之后额外免费附送一个碎片整理过程,内存整理的过程是无法并发的。空间碎片问题没有了,但停顿时间不得不变长了。虚拟机设计者们还提供了另外一个参数-XX: CMSFullGCsBeforeCompaction,这个参数用于设置在执行多少次不压缩的Full GC后,跟着来一次带压缩的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值