这里我们首先看下CMS并发网络周期正常完成的几个状态。
1、(STW)初始标记:这个阶段是标记从GcRoots直接可达的老年代工具、新生代引用的老年代工具,便是下图中灰色的点。这个过程是单线程的(JDK7之前单线程,JDK8之后并行,可以通过参数CMSParallelInitialMarkEnabled调度)。
2、并发标记:由上一个阶段标记过的工具,开始tracing过程,标记所有可达的工具,这个阶段垃圾回收线程和运用线程同时运行,如上图中的灰色的点。在并发标记过程中,运用线程还在跑,因此会导致有些工具会重新生代晋升到老年代、有些老年代的工具引用会被改变、有些工具会直接分配到老年代,这些受到影响的老年代工具所在的card会被标记为dirty,用于重新标记阶段扫描。这个阶段过程中,老年代工具的card被标记为dirty的可能缘故原由,便是下图中绿色的线:

3、预清理:预清理,也是用于标记老年代存活的工具,目的是为了让重新标记阶段的STW尽可能短。这个阶段的目标是在并发标记阶段被运用线程影响到的老年代工具,包括:(1)老年代中card为dirty的工具;(2)幸存区(from和to)中引用的老年代工具。因此,这个阶段也须要扫描新生代+老年代。【PS:会不会扫描Eden区的工具,我看源代码预测是没有,还须要连续求证】
4、可中断的预清理:这个阶段的目标跟“预清理”阶段相同,也是为了减轻重新标记阶段的事情量。可中断预清理的代价:在进入重新标记阶段之前只管即便等到一个Minor GC,只管即便缩短重新标记阶段的停顿韶光。其余可中断预清理会在Eden达到50%的时候开始,这时候离下一次minor gc还有半程的韶光,这个还有另一个意义,即避免短韶光内连着的两个停顿,如下图资料所示:
5、在预清理步骤后,如果知足下面两个条件,就不会开启可中断的预清理,直接进入重新标记阶段:
Eden的利用空间大于“CMSScheduleRemarkEdenSizeThreshold”,这个参数的默认值是2M;Eden的利用率大于即是“CMSScheduleRemarkEdenPenetration”,这个参数的默认值是50%。如果不知足上面两个条件,则进入可中断的预清理,可中断预清理可能会实行多次,那么退出这个阶段的出口有两个(源码拜会下图):设置了CMSMaxAbortablePrecleanLoops,并且实行的次数超过了这个值,这个参数的默认值是0;CMSMaxAbortablePrecleanTime,实行可中断预清理的韶光超过了这个值,这个参数的默认值是5000毫秒。如果是由于这个缘故原由退出,gc日志打印如下:有可能可中断预清理过程中一贯没等到Minor gc,这时候进入重新标记阶段的话,新生代还有很多活着的工具,就回导致STW变长,因此CMS还供应了CMSScavengeBeforeRemark参数,可以在进入重新标记之前逼迫进行依次Minor gc。
5、(STW)重新标记:重新扫描堆中的工具,进行可达性剖析,标记活着的工具。这个阶段扫描的目标是:新生代的工具 + Gc Roots + 前面被标记为dirty的card对应的老年代工具。如果预清理的事情没做好,这一步扫描新生代的时候就会花很多韶光,导致这个阶段的停顿韶光过长。这个过程是多线程的。
6、并发打消:用户线程被重新激活,同时将那些未被标记为存活的工具标记为不可达;
7、并发重置:CMS内部重置回收器状态,准备进入下一个并发回收周期。
CMS的非常情形
上面描述的是CMS的并发周期正常完成的情形,但是还有几种CMS并发周期失落败的情形:
并发模式失落败(Concurrent mode failure):CMS的目标便是在回收老年代工具的时候不要停滞全部运用线程,在并发周期实行期间,用户的线程依然在运行,如果这时候如果运用线程向老年代要求分配的空间超过预留的空间(包管失落败),就回触发concurrent mode failure,然后CMS的并发周期就会被一次Full GC代替——停滞全部运用进行垃圾网络,并进行空间压缩。如果我们设置了UseCMSInitiatingOccupancyOnly和CMSInitiatingOccupancyFraction参数,个中CMSInitiatingOccupancyFraction的值是70,那预留空间便是老年代的30%。晋升失落败:新生代做minor gc的时候,须要CMS的包管机制确认老年代是否有足够的空间容纳要晋升的工具,包管机制创造不足,则报concurrent mode failure,如果包管机制判断是够的,但是实际上由于碎片问题导致无法分配,就会报晋升失落败。永久代空间(或Java8的元空间)耗尽,默认情形下,CMS不会对永久代进行网络,一旦永久代空间耗尽,就回触发Full GC。三、CMS的调优针对停顿韶光过长的调优首先须要判断是哪个阶段的停顿导致的,然后再针对详细的缘故原由进行调优。利用CMS网络器的JVM可能引发停顿的情形有:(1)Minor gc的停顿;(2)并发周期里初始标记的停顿;(3)并发周期里重新标记的停顿;(4)Serial-Old网络老年代的停顿;(5)Full GC的停顿。个中并发模式失落败会导致第(4)种情形,晋升失落败和永久代空间耗尽会导致第(5)种情形。针对并发模式失落败的调优想办法增大老年代的空间,增加全体堆的大小,或者减少年轻代的大小以更高的频率实行后台的回收线程,即提高CMS并发周期发生的频率。设置UseCMSInitiatingOccupancyOnly和CMSInitiatingOccupancyFraction参数,调低CMSInitiatingOccupancyFraction的值,但是也不能调得太低,太低了会导致过多的无效的并发周期,会导致花费CPU韶光和更多的无效的停顿。常日来讲,这个过程须要几个迭代,但是还是有一定的套路,拜会《Java性能威信指南》中给出的建议,摘抄如下:> 对特定的运用程序,该标志的更优值可以根据 GC 日志中 CMS 周期首次启动失落败时的值得到。详细方法是,在垃圾回收日志中探求并发模式失落效,找到后再反向查找 CMS 周期最近的启动记录,然后根据日志来打算这时候的老年代空间占用值,然后设置一个比该值更小的值。增多回收线程的个数
CMS默认的垃圾网络线程数是(CPU个数 + 3)/4,这个公式的含义是:当CPU个数大于4个的时候,垃圾回收后台线程至少占用25%的CPU资源。举个例子:如果CPU核数是1-4个,那么会有1个CPU用于垃圾网络,如果CPU核数是5-8个,那么久会有2个CPU用于垃圾网络。针对永久代的调优如果永久代须要垃圾回收(或元空间扩容),就会触发Full GC。默认情形下,CMS不会处理永久代中的垃圾,可以通过开启CMSPermGenSweepingEnabled配置来开启永久代中的垃圾回收,开启后会有一组后台线程针对永久代做网络,须要把稳的是,触发永久代进行垃圾网络的指标跟触发老年代进行垃圾网络的指标是独立的,老年代的阈值可以通过CMSInitiatingPermOccupancyFraction参数设置,这个参数的默认值是80%。开启对永久代的垃圾网络只是个中的一步,还须要开启另一个参数——CMSClassUnloadingEnabled,使得在垃圾网络的时候可以卸载不用的类。四、CMS的trade-off是什么?上风低延迟的网络器:险些没有永劫光的停顿,运用程序只在Minor gc以及后台线程扫描老年代的时候发生极其短暂的停顿。劣势更高的CPU利用:必须有足够的CPU资源用于运行后台的垃圾网络线程,在运用程序线程运行的同时扫描堆的利用情形。【PS:现在做事器的CPU资源基本不是问题,这个点可以忽略】CMS网络器对老年代网络的时候,不再进行任何压缩和整理的事情,意味着老年代随着运用的运行会变得碎片化;碎片过多会影响大工具的分配,虽然老年代还有很大的剩余空间,但是没有连续的空间来分配大工具,这时候就会触发Full GC。CMS供应了两个参数来办理这个问题:(1)UseCMSCompactAtFullCollection,在要进行Full GC的时候进行内存碎片整理;(2)CMSFullGCsBeforeCompaction,每隔多少次不压缩的Full GC后,实行一次带压缩的Full GC。会涌现浮动垃圾;在并发清理阶段,用户线程仍旧在运行,必须预留出空间给用户线程利用,因此CMS比其他回收器须要更大的堆空间。五、几个问题的解答为什么ParNew可以和CMS合营利用,而Parallel Scanvenge不可以?答:这个跟Hotspot VM的历史有关,Parallel Scanvenge是不在“分代框架”下开拓的,而ParNew、CMS都是在分代框架下开拓的。CMS中minor gc和major gc是顺序发生的吗?答:不是的,可以交叉发生,即在并发周期实行过程中,是可以发生Minor gc的,这个找个gc日志就可以不雅观察到。CMS的并发网络周期得当触发?由下图可以看出,CMS 并发周期触发的条件有两个:
阈值检讨机制:老年代的利用空间达到某个阈值,JVM的默认值是92%(jdk1.5之前是68%,jdk1.6之后是92%),或者可以通过CMSInitiatingOccupancyFraction和UseCMSInitiatingOccupancyOnly两个参数来设置;这个参数的设置须要看运用处景,设置得太小,会导致CMS频繁发生,设置得太大,会导致过多的并发模式失落败。例如动态检讨机制:JVM会根据最近的回收历史,估算下一次老年代被耗尽的韶光,快到这个韶光的时候就启动一个并发周期。设置UseCMSInitiatingOccupancyOnly这个参数可以将这个特性关闭。CMS的并发网络周期会扫描哪些工具?会回收哪些工具?答:CMS的并发周期只会回收老年代的工具,但是在标记老年代的存活工具时,可能有些工具会被年轻代的工具引用,因此须要扫描全体堆的工具。CMS的gc roots包括哪些工具?答:首先,在JVM垃圾网络中Gc Roots的观点如何理解(拜会R大对GC roots的观点的阐明);第二,CMS的并发网络周期中,如何判断老年代的工具是活着?我们前面提到了,在CMS的并发周期中,仅仅扫描Gc Roots直达的工具会有遗漏,还须要扫描新生代的工具。如下图中的蓝色字体所示,CMS中的年轻代和老年代是分别网络的,因此在判断年轻代的工具存活的时候,须要把老年代当作自己的GcRoots,这时候并不须要扫描老年代的全部工具,而是利用了card table数据构造,如果一个老年代工具引用了年轻代的工具,则card中的值会被设置为分外的数值;反过来判断老年代工具存活的时候,也须要把年轻代当作自己的Gc Roots,这个过程我们在第三节已经论述过了。如果我的运用决定利用CMS网络器,推举的JVM参数是什么?我自己的运用利用的参数如下,是根据PerfMa的xxfox天生的,大家也可以利用这个产品调优自己的JVM参数:-Xmx4096M -Xms4096M -Xmn1536M -XX:MaxMetaspaceSize=512M -XX:MetaspaceSize=512M -XX:+UseConcMarkSweepGC -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 -XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses -XX:+CMSClassUnloadingEnabled -XX:+ParallelRefProcEnabled -XX:+CMSScavengeBeforeRemark -XX:ErrorFile=/home/admin/logs/xelephant/hs_err_pid%p.log -Xloggc:/home/admin/logs/xelephant/gc.log -XX:HeapDumpPath=/home/admin/logs/xelephant -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+HeapDumpOnOutOfMemoryErrorCMS干系的参数总结(须要把稳的是,这里我没有考虑太多JDK版本的问题,JDK1.7和JDK1.8这些参数的配置,有些默认值可能不一样,详细利用的时候还须要根据详细的版本来确认怎么设置)
| 编号 | 参数名称 | 阐明 || --- | --- | --- || 1 | UseConcMarkSweepGC | 启用CMS网络器 || 2 | UseCMSInitiatingOccupancyOnly | 关闭CMS的动态检讨机制,只通过预设的阈值来判断是否启动并发网络周期 || 3 | CMSInitiatingOccupancyFraction | 老年代空间占用到多少的时候启动并发网络周期,跟UseCMSInitiatingOccupancyOnly一起利用 || 4 | ExplicitGCInvokesConcurrentAndUnloadsClasses | 将System.gc()触发的Full GC转换为一次CMS并发网络,并且在这个网络周期中卸载 Perm(Metaspace)区域中不须要的类 || 5 | CMSClassUnloadingEnabled | 在CMS网络周期中,是否卸载类 || 6 | ParallelRefProcEnabled | 是否开启并发引用途理 || 7 | CMSScavengeBeforeRemark | 如果开启这个参数,会在进入重新标记阶段之前逼迫触发一次minor gc |参考资料
1、从实际案例聊聊Java运用的GC优化
https://tech.meituan.com/jvm_optimize.html
2、理解CMS垃圾回收日志
http://ifeve.com/jvm-cms-log/
3、图解CMS垃圾回收机制,你值得拥有
https://www.jianshu.com/p/2a1b2f17d3e4
4、为什么CMS虽然是老年代的gc,但仍要扫描新生代的?
https://www.zhihu.com/question/279580656/answer/408089811
5、R大对GC roots的观点的阐明
https://www.zhihu.com/question/53613423/answer/135743258
6、Introduce to CMS Collector
https://medium.com/@robiplus/introduce-to-cms-collector-47b4400665c3
7、《深入理解Java虚拟机》
8、《Java性能威信指南》
9、Oracle的GC调优手册
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/cms.html
10、what-is-the-threshold-for-cms-old-gc-to-be-triggered
https://stackoverflow.com/questions/33557644/what-is-the-threshold-for-cms-old-gc-to-be-triggered
11、Frequently Asked Questions about Garbage Collection in the Hotspot Java VirtualMachine
https://www.oracle.com/technetwork/java/faq-140837.html
12、Java SE HotSpot at a Glance
https://www.oracle.com/technetwork/java/javase/tech/index-jsp-136373.html
13、xxfox:PerfMa的参数调优神器
http://xxfox.perfma.com/
14、详解CMS垃圾回收机制
https://www.cnblogs.com/littleLord/p/5380624.html
15、ParNew和PSYoungGen和DefNew是一个东西么?
http://hllvm.group.iteye.com/group/topic/37095
16、Java SE的内存管理白皮书
https://www.oracle.com/technetwork/java/javase/memorymanagement-whitepaper-150215.pdf
17、Garbage Collection in Elasticsearch and the G1GC
https://medium.com/naukri-engineering/garbage-collection-in-elasticsearch-and-the-g1gc-16b79a447181
18、A Heap of Trouble
https://www.elastic.co/cn/blog/a-heap-of-trouble
19、毕玄的文章:为什么不建议
http://hellojava.info/
20、JVM源码剖析之SystemGC完备解读
http://lovestblog.cn/blog/2015/05/07/system-gc/
读者谈论关于CMS网络器的回收范围,下面这张图是有误导的,从官方文档上看来,CMS网络器包括年轻代和老年代的网络,只不过对年轻代的网络的策略和ParNew相同,这个可以从参考资料16的第11页看到。concurrent mode failure和promotion failed触发的Full GC有啥不同?(这个问题是我、阿飞、蒋晓峰一起谈论的结果)答:concurrent mode failure触发的\"大众Full GC\公众不是我们常说的Full GC——正常的Full GC实在是全体gc过程包括ygc和cms gc。也便是说,这个问题本身是有问题的,concurrent mode failure的时候触发的并不是我们常说的Full GC。然后再去谈论一个遗漏的知识点:CMS gc的并发周期有两种模式:foreground和background。concurrent mode failure触发的是foreground模式,会停息全体运用,会将一些并行的阶段省却做一次老年代网络,行为跟Serial-Old的一样,至于在这个过程中是否须要压缩,则须要看三个条件:(1)我们设置了UseCMSCompactAtFullCollection和CMSFullGCsBeforeCompaction,前者设置为true,后者默认是0,前者表示是在Full GC的时候实行压缩,后者表示是每隔多少个进行压缩,默认是0的话便是每次Full GC都压缩;(2)用户调用了System.gc(),而且DisableExplicitGC没有开启;(3)young gen报告接下来如果做增量收集会失落败。
promotion failed触发的是我们常说的的Full GC,对年轻代和老年代都会回收,并进行整理。promotion failed和concurrent mode failure的触发缘故原由有啥不同?promotion failed是说,包管机制确定老年代是否有足够的空间容纳新来的工具,如果包管机制说有,但是真正分配的时候创造由于碎片导致找不到连续的空间而失落败;concurrent mode failure是指并发周期还没实行完,用户线程就来要求比预留空间更大的空间了,即后台线程的网络没有遇上运用线程的分配速率。什么情形下才选择利用CMS网络器呢?我之前的不雅观念是:小于8G的都用CMS,大于8G的选择G1。蒋晓峰跟我谈论了下这个不雅观念,提出了一些别的想法,我以为也有道理,记录在这里:除了看吞吐量和延时,还须要看详细的运用,比方说ES,Lucene和G1是不兼容的,因此默认的网络器便是CMS,详细见可参考资料17和18。小于3G的堆,如果不是对延迟有特殊高的需求,不建议利用CMS,紧张是由于CMS的几个缺陷导致的:(1)并发周期的触发比例不好设置;(2)抢占CPU韶光;(3)包管判断导致YGC变慢;(4)碎片问题,更详细的谈论拜会资料19。作者:杜琪