CMS GC算法相对于其他算法来讲要复杂很多,主要过程如下:
CMS GC也被称为低延迟GC。它经常被用在那些对于响应时间要求十分苛刻的应用之上。 当然,这种GC类型在拥有stop-the-world时间很短的优点的同时,也有如下缺点:
-XX:+UseConcMarkSweepGC: 新生代使用并行收集器,老年代使用 CMS+串行收集器。
-XX:+ParallelCMSThreads: 设定 CMS 的线程数量。
-XX:+CMSInitiatingOccupancyFraction:设置 CMS 收集器在老年代空间被使用多少后触发,默认为 68%。
-XX:+UseFullGCsBeforeCompaction:设定进行多少次 CMS 垃圾回收后,进行一次内存压缩。
-XX:+CMSClassUnloadingEnabled:允许对类元数据进行回收。
-XX:+CMSParallelRemarkEndable:启用并行重标记。
-XX:CMSInitatingPermOccupancyFraction:当永久区占用率达到这一百分比后,启动 CMS 回收 (前提是-XX:+CMSClassUnloadingEnabled 激活了)。
-XX:UseCMSInitatingOccupancyOnly:表示只在到达阈值的时候,才进行 CMS 回收。
-XX:+CMSIncrementalMode:使用增量模式,比较适合单 CPU。
-XX:+UseCMSCompactAtFullCollection 在FULL GC的时候,对年老代进行压缩,CMS是不会移动内存的,会产生碎片造成内存不够,增加这个参数是个好习惯,可能会影响性能,但是可以消除碎片
2016-06-26T02:16:33.814+0800: 225000.127: [GC [1 CMS-initial-mark: 4109808K(4194304K)] 4249081K(5033216K), 0.1662490 secs] [Times: user=0.16 sys=0.00, real=0.16 secs]
初始标记:会暂停应用,单线程只标记根能直接可达的对象,很快。
2016-06-26T02:16:33.981+0800: 225000.293: [CMS-concurrent-mark-start]
2016-06-26T02:16:34.411+0800: 225000.723: [CMS-concurrent-mark: 0.430/0.430 secs] [Times: user=0.64 sys=0.04, real=0.43 secs]
并发标记:不会暂停应用
2016-06-26T02:16:34.411+0800: 225000.724: [CMS-concurrent-preclean-start]
2016-06-26T02:16:34.471+0800: 225000.783: [CMS-concurrent-preclean: 0.058/0.060 secs] [Times: user=0.08 sys=0.01, real=0.06 secs]
预清理:不会暂停应用
该阶段检查并发标记阶段时从新生代晋升的对象,或新分配的对象,或被应用程序线程更新过的对象,帮助减少重新标记阶段的暂停时间。
2016-06-26T02:16:34.471+0800: 225000.784: [CMS-concurrent-abortable-preclean-start]
CMS: abort preclean due to time 2016-06-26T02:16:39.872+0800: 225006.184: [CMS-concurrent-abortable-preclean: 5.397/5.400 secs] [Times: user=7.19 sys=0.55, real=5.40 secs]
可终止的预清理:不会暂停应用
继续预清理,至到Eden区占用量达到CMSScheduleRemarkEdenPenetration(默认50%),或达到5秒钟。
2016-06-26T02:16:39.872+0800: 225006.185: [GC[YG occupancy: 196297 K (838912 K)]
2016-06-26T02:16:39.872+0800: 225006.185: [Rescan (parallel) , 0.0875580 secs]
2016-06-26T02:16:39.960+0800: 225006.272: [weak refs processing, 0.0000670 secs]
2016-06-26T02:16:39.960+0800: 225006.272: [class unloading, 0.0052810 secs]
2016-06-26T02:16:39.965+0800: 225006.278: [scrub symbol table, 0.0025170 secs]
2016-06-26T02:16:39.968+0800: 225006.280: [scrub string table, 0.0005470 secs] [1 CMS-remark: 4109808K(4194304K)] 4306106K(5033216K), 0.0975850 secs] [Times: user=0.24 sys=0.01, real=0.10 secs]
重新标记:会暂停应用,多线程,通过ParallelGCThreads指定,此阶段是CMS中暂停时间最长的。
重新从根扫描新生代中剩余的更新过的对象,也会处理其引用对象。
2016-06-26T02:16:39.970+0800: 225006.283: [CMS-concurrent-sweep-start]
2016-06-26T02:16:40.361+0800: 225006.674: [CMS-concurrent-sweep: 0.391/0.391 secs] [Times: user=0.55 sys=0.03, real=0.39 secs]
并发清理:不会暂停应用
2016-06-26T02:16:40.361+0800: 225006.674: [CMS-concurrent-reset-start]
2016-06-26T02:16:40.373+0800: 225006.685: [CMS-concurrent-reset: 0.011/0.011 secs] [Times: user=0.02 sys=0.01, real=0.01 secs]
重置:cms数据结构重新初始化,为下一次cms准备。
出现此现象的原因主要有两个:
if the concurrent collector is unable to finish reclaiming the unreachable objects before the tenured generation fills up, or if an allocation cannot be satisfied with the available free space blocks in the tenured generation, then the application is paused and the collection is completed with all the application threads stopped
最后总结下,出现Concurrent ModeFailure现象时,解决办法就是要让年老代留有足够的空间,以保证新对象空间的分配。另外在JVM BUG中有提到,JDK1.5_09版本之前,JVM参数-XX:CMSInitiatingOccupancyFraction是无效。
为什么会发生Promotion Failed
解决办法类似,也是调整年轻代和年老代的比例,还有CMS GC的时机。
对于采用CMS进行旧生代GC的程序而言,尤其要注意GC日志中是否有promotion failed和concurrent mode failure两种状况,当这两种状况出现时可能会触发Full GC。
总结一句话:使用标记整理清除碎片和提早进行CMS操作。
网易云新用户大礼包:https://www.163yun.com/gift
本文来自网易实践者社区,经作者侯本文授权发布。