java CMS gc解析

转载: http://www.blogjava.net/killme2008/archive/2009/09/22/295931.html
    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算法是一致的。

下面是参数介绍和遇到的问题总结,

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.年轻代的并行收集线程数默认是(ncpus <= 8) ? ncpus : 3 + ((ncpus * 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(
2166784K), 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。

庄周梦蝶

生活、程序、未来

::
首页 ::
 ::
 ::
聚合
 ::
管理

公告

关于我










随笔分类

友情链接

最新随笔

搜索

最新评论

CMS gc实践总结(纠正并发线程数)

Posted on 2009-09-22 02:10 dennis 阅读(5488) 评论(1)  编辑  收藏 所属分类: java

首先感谢阿宝同学的帮助,我才对这个gc算法的调整有了一定的认识,而不是停留在过去仅仅了解的阶段。在读过sun的文档和跟阿宝讨论之后,做个小小的总结。
    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算法是一致的。

下面是参数介绍和遇到的问题总结,

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.年轻代的并行收集线程数默认是(ncpus <= 8) ? ncpus : 3 + ((ncpus * 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(
2166784K), 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。

参考资料:
JDK5.0垃圾收集优化之--Don‘t Pause》 by 江南白衣
《记一次Java GC调整经历》1,2 by Arbow
Java SE 6 HotSpot[tm] Virtual Machine Garbage Collection Tuning
Tuning Garbage Collection
with the 5.0 JavaTM Virtual Machine

评论

# re: CMS gc实践总结  回复  更多评论

2009-09-22 11:42 by Scorpio Zhen

不错,更深入的学习gc方面的知识

新用户注册  刷新评论列表

 
 
     
  找优秀程序员,就在博客园

标题  
姓名  
主页  
 
验证码 *    
内容(请不要发表任何与政治相关的内容)

 
Remember Me?
 
   
登录      
[使用Ctrl+Enter键可以直接提交]
 
 
 
网站导航:

博客园  
IT新闻  
知识库  
C++博客  
程序员招聘  
管理


相关文章:

 
 
 

Powered by:

BlogJava

Copyright © dennis

时间: 2024-10-10 10:55:38

java CMS gc解析的相关文章

JVM学习(4)——全面总结Java的GC算法和回收机制---转载自http://www.cnblogs.com/kubixuesheng/p/5208647.html

俗话说,自己写的代码,6个月后也是别人的代码--复习!复习!复习!涉及到的知识点总结如下: 一些JVM的跟踪参数的设置 Java堆的分配参数 -Xmx 和 –Xms 应该保持一个什么关系,可以让系统的性能尽可能的好呢?是不是虚拟机内存越大越好? Java 7之前和Java 8的堆内存结构 Java栈的分配参数 GC算法思想介绍 –GC ROOT可达性算法 –标记清除 –标记压缩 –复制算法 可触及性含义和在Java中的体现 finalize方法理解 Java的强引用,软引用,弱引用,虚引用 GC

也学习Java/JVM/GC (三)

GC算法 目前HotSpot的GC算法是针对分代的GC算法,主要包括串行GC.并行GC.CMS GC和G1. 一.串行GC 串行GC可以在JVM的启动参数上加-XX:+UseSerialGC这个非标准化参数实现.串行GC会暂停应用,进行垃圾回收.多数运行在客户端机器并且 没有短暂停时间要求的应用使用串行GC.串行GC仅利用了一个虚拟的处理器进行垃圾回收. 在应用中使用串行GC命令: java -Xms20m -Xmx20m -Xmn10m -XX:+UseSerialGC -XX:+PrintG

也学习Java/JVM/GC(四)

GC日志分析 程序代码: 1 public class JvmTest { 2 public static void main(String[] args) { 3 4 int m = 1024 * 1024; 5 byte[] b = new byte[2 * m]; 6 System.out.println("allocate 2M"); 7 8 byte[] b1 = new byte[2 * m]; 9 System.out.println("allocate 2M&

【转载】为什么不建议&lt;=3G的情况下使用CMS GC

之前曾经有讲过在heap size<=3G的情况下完全不要考虑CMS GC,在heap size>3G的情况下也优先选择ParallelOldGC,而不是CMS GC,只有在暂停时间无法接受的情况下才考虑CMS GC(不过当然,一般来说在heap size>8G后基本上都得选择CMS GC,否则那暂停时间是相当吓人的,除非是完全不在乎响应时间的应用),这其实也是官方的建议(每年JavaOne的GC Tuning基本都会这么讲). 为什么给了一个这么“武断”的建议呢,不是我对CMS GC有

老年代 CMS gc回收算法 对hbase的影响

老年代 CMS gc回收算法 对hbase的影响***** 参考链接: 深入研究java gc https://blog.51cto.com/12445535/2372976CMS失败模式(CMS Failure Mode)1.上文提到在正常的情况下CMS整个流程的暂停时间都是很短的,一般也就在10ms-100ms左右.2.然而这与线上的情况并不相符,线上集群在读写压力很大的情况下,经常会出现长时间的卡顿,有些卡顿甚至长达几分钟,导致很严重的读写阻塞,甚至会造成Region Server和Zoo

你不知道的CMS GC

在G1出来之前,CMS绝对是OLTP系统的标配.即使G1出来几年了,生产环境很多的JVM实例还是采用ParNew+CMS的组合.但是即使其得到这么广泛的应用,还是有很多同学对它有很深的误解.本文主要对ParNew+CMS经典组合下,触发的几种垃圾回收方式进行几个概念的纠正. Backgroud 可能更多人只知道CMS,而不知道Backgroud CMS.事实上我们说的CMS,即包含了5个阶段的CMS,就是Background CMS,如下图所示: 说明: 图中初始化标记阶段是串行的,这是JDK7

Java默认GC

各个版本对应的垃圾收集器: Java 7 - Parallel GC Java 8 - ParallelGC Java 9 - G1 GC Java 10- G1 GC Java 11- ZGC (参见:ZGC)注:2017年不再维护CMS,参见Deprecate the Concurrent Mark Sweep  ,并且CMS在最新的jdk14 build#23版本中已经不能使用,看来官方在极力推进G1,ZGC等收集器的使用 原文地址:https://www.cnblogs.com/hsuc

开源 免费 java CMS - FreeCMS1.9 站内信

项目地址:http://www.freeteam.cn/ 站内信 管理站内信数据. 1. 站内信 从左侧管理菜单点击站内信进入. 2. 发送 系统管理员可以直接发送站内信给会员. 点击"发送"按钮. 选择需要发送的会员,输入标题内容后点击"发送"按钮. 3. 查看 在站内信列表中直接点击标题可以查看站内信的详细信息. 4. 删除 选择需要删除的站内信,然后点击"删除"按钮. 提示:同时可以删除多个站内信. 为了防止误操作,系统会提示用户是否删除,

开源 免费 java CMS - FreeCMS1.9 移动APP生成栏目列表数据

项目地址:http://www.freeteam.cn/ 生成栏目列表数据 提取当前管理站点下允许移动APP访问的栏目列表,生成json数据到/site/站点目录/mobile/channels.html页面. 从左侧管理菜单点击生成栏目列表数据进入. 点击确定即可. 开源 免费 java CMS - FreeCMS1.9 移动APP生成栏目列表数据,布布扣,bubuko.com