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

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

一、启用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.年轻代的并行收集线程数默认是(ncpus <= 8) ? ncpus : 3 + ((ncpus * 5) / 8),若是你但愿设定这个线程数,能够经过-XX:ParallelGCThreads= N 来调整。

8.进入重点,在初步设置了一些参数后,例如:
php

- 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的日志输出相似这样:
html

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的日志输出大概是这样:
java

 [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。node

 

庄周梦蝶

生活、程序、将来
   :: 首页 ::  ::  :: 聚合  :: 管理
 

最新评论

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

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

一、启用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.年轻代的并行收集线程数默认是 (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方面的知识
新用户注册   刷新评论列表  

 
 
 
     
  找优秀程序员,就在博客园
标题  
姓名  
主页  
 
验证码 *    
内容(请不要发表任何与政治相关的内容)
 
 
相关文章
相关标签/搜索