CMS收集器

CMS收集周期

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算法是一致的。

参数介绍

1、启用CMS:-XX:+UseConcMarkSweepGC。
2。CMS默认启动的回收线程数目是 (ParallelGCThreads + 3)/4) ,如果你需要明确设定,可以通过-XX:ParallelCMSThreads=20来设定,其中ParallelGCThreads是年轻代的并行收集线程数
3、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] 

这个问题的产生是由于救助空间不够,从而向年老代转移对象,年老代没有足够的空间来容纳这些对象,导致一次full gc的产生。解决这个问题的办法有两种完全相反的倾向:增大救助空间、增大年老代或者去掉救助空间。 增大救助空间就是调整-XX:SurvivorRatio参数,这个参数是Eden区和Survivor区的大小比值,默认是32,也就是说Eden区是 Survivor区的32倍大小,要注意Survivo是有两个区的,因此Surivivor其实占整个young genertation的1/34。调小这个参数将增大survivor区,让对象尽量在survitor区呆长一点,减少进入年老代的对象。去掉救助空 间的想法是让大部分不能马上回收的数据尽快进入年老代,加快年老代的回收频率,减少年老代暴涨的可能性,这个是通过将-XX:SurvivorRatio 设置成比较大的值(比如65536)来做到。在我们的应用中,将young generation设置成256M,这个值相对来说比较大了,而救助空间设置成默认大小(1/34),从压测情况来看,没有出现prommotion failed的现象,年轻代比较大,从GC日志来看,minor gc的时间也在5-20毫秒内,还可以接受,因此暂不调整。

Concurrent mode failed的产生是由于CMS回收年老代的速度太慢,导致年老代在CMS完成前就被沾满,引起full gc,避免这个现象的产生就是调小-XX:CMSInitiatingOccupancyFraction参数的值,让CMS更早更频繁的触发,降低年老代被沾满的可能。我们的应用暂时负载比较低,在生产环境上年老代的增长非常缓慢,因此暂时设置此参数为80。在压测环境下,这个参数的表现还可以,没有出现过Concurrent mode failed。

http://www.importnew.com/13954.html

时间: 2024-09-30 23:48:26

CMS收集器的相关文章

JAVA垃圾收集器之CMS收集器

1.特点 CMS收集器是JAVA虚拟机中垃圾收集器的一种.它运行在JAVA虚拟机的老年代中.CMS是(Concurrent MarkSweep)的首字母缩写.CMS收集器是一种以获取最短回收停顿时间为目标的收集器.比较适用于互联网等场合,可能是互联网中最重要的收集器模式: 2.优点 由于整个过程中耗时最长的并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作,所以总体上来说,CMS收集器的内存回收过程是与用户线程一起并发地执行的.因此CMS是一款优秀的收集器,具备了并发收集.低停顿的优点

CMS收集器和G1收集器

CMS收集器 CMS收集器是一种以获取最短回收停顿时间为目标的收集器.基于"标记-清除"算法实现,它的运作过程如下: 初始标记 并发标记 重新标记 并发清除 初始标记.从新标记这两个步骤仍然需要"stop the world",初始标记仅仅只是标记一下GC Roots能直接关联到的对象,熟读很快,并发标记阶段就是进行GC Roots Tracing,而重新标记阶段则是为了修正并发标记期间因用户程序继续运作而导致标记产生表动的那一部分对象的标记记录,这个阶段的停顿时间

JVM快速调优手册之五: ParNew收集器+CMS收集器的产品案例分析(响应时间优先)

服务器 双核,4个cores; 16G memory [[email protected] ~]# cat /proc/cpuinfo | grep "cpu cores" cpu cores : 2 cpu cores : 2 公式简述 响应时间优先的并发收集器,主要是保证系统的响应时间,减少垃圾收集时的停顿时间.适用于应用服务器.电信领域等. ParNew收集器 ParNew收集器是Serial收集器的多线程版本,许多运行在Server模式下的虚拟机中首选的新生代收集器,除Seri

JVM之CMS收集器

CMS(Concurrent Mark Sweep) 最短回收停顿,适合维持响应时间上的要求. 初始标记 Initial mark:标记GC Roots能够关联到的对象.stop-mark. 并发标记 Concurrent mark:GC Roots tracing,并行业务线程. 重新标记 remark:重新标记并发标记过程中因为用户程序同时运行而导致标记产生变动的对象的标记记录.stop-mark. 并发清除 Concurrent sweep:清除无用对象. 初始标记和重新标记占用时间比较短

并发标记扫描(CMS)收集器(待完成)

并发标记扫描(CMS)收集器专为需要较短垃圾收集暂停时间且能够在应用程序运行时与垃圾收集器共享处理器资源的应用程序而设计. 对于任何暂停时间要求较低的应用程序,应考虑使用此收集器. 使用命令行选项启用CMS收集器-XX:+UseConcMarkSweepGC. 与其他可用的收集器类似,CMS收集器是分代的;;因此,小型和主要收集都会发生 CMS收集器尝试通过使用单独的垃圾收集器线程在执行应用程序线程的同时,跟踪可访问的对象来减少由于主要收集而导致的暂停时间. 在每个主要的收集周期中,CMS收集器

JAVA G1收集器 第11节

JAVA G1收集器 第11节 上两章我们讲了新生代和年老代的收集器,那么这一章的话我们就要讲一个收集范围涵盖整个堆的收集器——G1收集器. 先讲讲G1收集器的特点,他也是个多线程的收集器,能够充分利用多个CPU进行工作,收集方式也与CMS收集器类似,因此不会有太久的停顿. 虽然回收的范围是整个堆,但还是有分代回收的回收方式.在年轻代依然采用复制算法:年老代也同样采用“标记-清除-整理”算法.但是,新生代与老年代在堆内存中的布局就和以往的收集器有着很大的区别:G1将整个堆分成了一个个大小相等的独

JVM-垃圾收集器

本篇主要介绍一下JVM的几种垃圾收集器.其示意图如下所示,上面的三个是新生代的收集器,下面三个是老年代的收集器,其中G1收集器是可以用于新生代和老年代的收集工作. JVM垃圾收集器 1.Serial(串行收集器):用于新生代GC,复制算法,启动时默认为Client模式,GC的线程为单线程.缺点是因为单线程GC,会造成中断的时间(Sop-the-world)比较大.(对于单核的CPU来说,比并行收集的效率要高)其示意图如下所示: Serial串行收集器 2.ParNew(并行收集器):与Seria

JVM收集器概述

Serial 收集器 Serial 是一个新生代收集器 :单线程垃圾回收器,采用复制算法 -XX:+UseSerialGC ParNew 收集器 ParNew 是一个新生代收集器:ParNew收集器其实是Serial收集器的多线程版本,其余行为包括Serial收集器可用的所有控制参数.收集算法.stop the world.对象分配规则,回收策略等都与Serial收集器完全一样: 控制参数有如下: -XX:+UseParNewGC -XX:SurvivorRatio //新生代中Edge区域Su

【5】JVM-垃圾收集器

通过学习了解到现在商用的JVM中的垃圾收集采用的是分代收集算法,即针对不同年代采用不同的收集算法.在JVM中,GC主要作用于堆内存中,堆内存又被划分为新生代和老年代,由于新生代对象绝大多数是朝生夕死,而老年代相对存活时间就很长,故而需要使用不同的垃圾收集机制,所以垃圾收集器也就分为新生代收集器和老年代收集器,两者相互组合进行JVM堆内存的空间回收(下图中相连的垃圾收集器表示可以相互组合,注意Serial Old和CMS也可以联合进行老年代的垃圾收集).JDK6u14中开始测试的G1垃圾收集器,正