CMS,全称Concurrent Low Pause Collector,是jdk1.4后期版本开始引入的新gc算法,在jdk5和jdk6中获得了进一步改进,它的主要适合场景是对响应时间的重要性需求 大于对吞吐量的要求,可以承受垃圾回收线程和应用线程共享处理器资源,而且应用中存在比较多的长生命周期的对象的应用。CMS是用于对tenured generation的回收,也就是年老代的回收,目标是尽可能减小应用的暂停时间,减小full gc发生的概率,利用和应用程序线程并发的垃圾回收线程来标记清除年老代。在咱们的应用中,由于有缓存的存在,而且对于响应时间也有比较高的要求,所以希 望能尝试使用CMS来替代默认的server型JVM使用的并行收集器,以便得到更短的垃圾回收的暂停时间,提升程序的响应性。算法
CMS并不是没有暂停,而是用两次短暂停来替代串行标记整理算法的长暂停,它的收集周期是这样:缓存
初始标记(CMS-initial-mark) -> 并发标记(CMS-concurrent-mark) -> 从新标记(CMS-remark) -> 并发清除(CMS-concurrent-sweep) ->并发重设状态等待下次CMS的触发(CMS-concurrent-reset)。 其中的1,3两个步骤须要暂停全部的应用程序线程的。第一次暂停从root对象开始标记存活的对象,这个阶段称为初始标记;第二次暂停是在并发标记以后, 暂停全部应用程序线程,从新标记并发标记阶段遗漏的对象(在并发标记阶段结束后对象状态的更新致使)。第一次暂停会比较短,第二次暂停一般会比较长,而且 remark这个阶段能够并行标记。多线程
而并发标记、并发清除、并发重设阶段的所谓并发,是指一个或者多个垃圾回收线程和应用程序线程并发地运行,垃圾回收线程不会暂停应用程序的执行,若是你有多于一个处理器,那么并发收集线程将与应用线程在不一样的处理器上运行,显然,这样的开销就是会下降应用的吞吐量。Remark阶段的并行,是指暂停了全部应用程序后,启动必定数目的垃圾回收进程进行并行标记,此时的应用线程是暂停的。并发
CMS的young generation的回收采用的仍然是并行复制收集器,这个跟Paralle gc算法是一致的。性能
该阶段进行可达性分析,标记GC ROOT能直接关联到的对象。 注意是直接关联间接关联的对象在下一阶段标记。线程
该阶段进行GC ROOT TRACING,在第一个阶段被暂停的线程从新开始运行。日志
由前阶段标记过的对象出发,全部可到达的对象都在本阶段中标记。code
既然类似为何要有这一步?server
前面咱们讲过,CMS是以获取最短停顿时间为目的的GC。对象
重标记须要STW(Stop The World),所以重标记的工做尽量多的在并发阶段完成来减小STW的时间。
此阶段标记重新生代晋升的对象、新分配到老年代的对象以及在并发阶段被修改了的对象。
有了前面的基础,这个阶段的工做量被大大减轻,停顿时间所以也会减小。
注意这个阶段是多线程的。
一、启用CMS:-XX:+UseConcMarkSweepGC。 2。CMS默认启动的回收线程数目是 (ParallelGCThreads + 3)/4) ,若是你须要明确设定,能够经过-XX:ParallelCMSThreads=20来设定,其中ParallelGCThreads是年轻代的并行收集线程数 三、CMS是不会整理堆碎片的,所以为了防止堆碎片引发full gc,经过会开启CMS阶段进行合并碎片选项:-XX:+UseCMSCompactAtFullCollection,开启这个选项必定程度上会影响性能,阿宝的blog里说也许能够经过配置适当的CMSFullGCsBeforeCompaction来调整性能,未实践。 4.为了减小第二次暂停的时间,开启并行remark: -XX:+CMSParallelRemarkEnabled。若是remark仍是过长的话,能够开启-XX:+CMSScavengeBeforeRemark选项,强制remark以前开始一次minor gc,减小remark的暂停时间,可是在remark以后也将当即开始又一次minor gc。
5.为了不Perm区满引发的full gc,建议开启CMS回收Perm区选项: +CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabled
6.默认CMS是在tenured generation沾满68%的时候开始进行CMS收集,若是你的年老代增加不是那么快,而且但愿下降CMS次数的话,能够适当调高此值: -XX:CMSInitiatingOccupancyFraction=80
这里修改为80%沾满的时候才开始CMS回收。
7.年轻代的并行收集线程数默认是(cpu <= 8) ? cpu : 3 + ((cpu * 5) / 8),若是你但愿下降这个线程数,能够经过-XX:ParallelGCThreads= N 来调整。
8.进入重点,在初步设置了一些参数后,例如:
-server -Xms1536m -Xmx1536m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=64m -XX:MaxPermSize=64m -XX:-UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingOccupancyFraction=80 -XX:+CMSParallelRemarkEnabled -XX:SoftRefLRUPolicyMSPerMB=0
须要在生产环境或者压测环境中测量这些参数下系统的表现,这时候须要打开GC日志查看具体的信息,所以加上参数:
-verbose:gc -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:/home/test/logs/gc.log
在运行至关长一段时间内查看CMS的表现状况,CMS的日志输出相似这样:
4391.322: [GC [1 CMS-initial-mark: 655374K(1310720K)] 662197K(1546688K), 0.0303050 secs] [Times: user=0.02 sys=0.02, real=0.03 secs] 4391.352: [CMS-concurrent-mark-start] 4391.779: [CMS-concurrent-mark: 0.427/0.427 secs] [Times: user=1.24 sys=0.31, real=0.42 secs] 4391.779: [CMS-concurrent-preclean-start] 4391.821: [CMS-concurrent-preclean: 0.040/0.042 secs] [Times: user=0.13 sys=0.03, real=0.05 secs] 4391.821: [CMS-concurrent-abortable-preclean-start] 4392.511: [CMS-concurrent-abortable-preclean: 0.349/0.690 secs] [Times: user=2.02 sys=0.51, real=0.69 secs] 4392.516: [GC[YG occupancy: 111001 K (235968 K)]4392.516: [Rescan (parallel) , 0.0309960 secs]4392.547: [weak refs processing, 0.0417710 secs] [1 CMS-remark: 655734K(1310720K)] 766736K(1546688K), 0.0932010 secs] [Times: user=0.17 sys=0.00, real=0.09 secs] 4392.609: [CMS-concurrent-sweep-start] 4394.310: [CMS-concurrent-sweep: 1.595/1.701 secs] [Times: user=4.78 sys=1.05, real=1.70 secs] 4394.310: [CMS-concurrent-reset-start] 4394.364: [CMS-concurrent-reset: 0.054/0.054 secs] [Times: user=0.14 sys=0.06, real=0.06 secs]
其中能够看到CMS-initial-mark阶段暂停了0.0303050秒,而CMS-remark阶段暂停了0.0932010秒,所以两次暂停的总共时间是0.123506秒,也就是123毫秒左右。两次短暂停的时间之和在200如下能够称为正常现象。
可是你极可能遇到两种fail引发full gc:Prommotion failed和Concurrent mode failed。
Prommotion failed的日志输出大概是这样:
[ParNew (promotion failed): 320138K->320138K(353920K), 0.2365970 secs]42576.951: [CMS: 1139969K->1120688K( 166784K), 9.2214860 secs] 1458785K->1120688K(2520704K), 9.4584090 secs]