GC参数

GC参数

大纲:

n  堆的回顾

n  串行收集器

n  并行收集器

n  CMS收集器

n  Tomcat实例演示

堆的回顾(堆内存结构图)

GC参数

串行收集器(UseSerialGC)新生代+老年代

串行收集器的特点

n  最古老,最稳定

n  效率高

可能会产生较长的停顿

-XX:+UseSerialGC

–      新生代、老年代使用串行回收

–      新生代复制算法

–      老年代标记-压缩

串行收集器的工作示意图

GC参数

串行回收器在回收的时候只使用一个线程去回收,在多核的情况下,没办法很好的发挥多核的性能。所以可能会产生较长时间的停顿。

GC日志:

0.844: [GC 0.844: [DefNew(新生代): 17472K->2176K(19648K), 0.0188339 secs] 17472K->2375K(63360K), 0.0189186 secs] [Times: user=0.01 sys=0.00, real=0.02 secs]

8.259: [Full GC 8.259: [Tenured(老年代): 43711K->40302K(43712K), 0.2960477 secs] 63350K->40302K(63360K), [Perm(方法区) : 17836K->17836K(32768K)], 0.2961554 secs] [Times: user=0.28 sys=0.02, real=0.30 secs]

并行收集器

ParNew并行收集器(新生代)

特点:

n  ParNew

–      -XX:+UseParNewGC

•       新生代并行

•       老年代串行

–      Serial收集器新生代的并行版本

–      复制算法

–      多线程,需要多核支持

–      -XX:ParallelGCThreads 参数限制线程数量

并行回收器只针对新生代。全名是串行收集器在新生代的并行版本。

它只是简单地将串行回收器多线程化,它的回收策略、算法以及参数和新生代串行回收器一样。 

多线程不一定快,要看cpu的情况,如果是多核的cpu的情况,可能会快一些,当然,也需要配置一个合理的线程的数量。如果是单核cpu的情况,建议还是采用串行回收器。一般设置的线程数量于等于可用的CPU的数量,不宜设置过大。当CPU个数大于8个的时候,工作现场个数最佳值为:3+((5*CPU_Count)/8)。

并行与并发:并行是开多个线程一起做一个事情(多个线程一起做,你做你的,我做我的,彼此不冲突)。比如开多个GC线程收集垃圾。并发是多个线程在做多个不同的事情,但是它们都没有暂停,而是交替执行。(多个线程一起做,彼此之间操作的资源有重叠,产生了资源竞争。)比如GC线程和应用线程交替执行,一起竞争cpu资源。

工作示意图:

GC参数

GC日志:

0.834: [GC 0.834: [ParNew(新生代并行收集器,并行收集器只作用于新生代): 13184K->1600K(14784K), 0.0092203 secs] 13184K->1921K(63936K), 0.0093401 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

Parallel并行收集器(新生代+可设置老年代)

特点:

n  Parallel(并行)收集器

–      类似ParNew

–      新生代复制算法

–      老年代 标记-压缩

–     更加关注吞吐量

–      -XX:+UseParallelGC

•       使用Parallel(并行)收集器+ 老年代串行

–      -XX:+UseParallelOldGC

•       使用Parallel(并行)收集器+ 并行老年代

工作示意图:

GC参数

GC日志:

1.500: [Full GC [PSYoungGen: 2682K->0K(19136K)] [ParOldGen: 28035K->30437K(43712K)] 30717K->30437K(62848K) [PSPermGen: 10943K->10928K(32768K)], 0.2902791 secs] [Times: user=1.44 sys=0.03, real=0.30 secs]

 

停顿时间和吞吐量调优参数:

-XX:MaxGCPauseMills

–      设置最大停顿时间,单位毫秒(-XX:MaxGCPauseMillis=200)

–      GC尽力保证回收时间不超过设定值

-XX:GCTimeRatio

–      0-100的取值范围(-XX:GCTimeRatio=19)

–      设置垃圾收集时间占总时间的比(吞吐量),即应用程序线程活动的时间占整个程序运行时间的比值。

–      默认99,即最大允许1%时间做GC

n  这两个参数是矛盾的。因为停顿时间和吞吐量[z1] 不可能同时调优。

GC很频繁,最大停顿时间变短,吞吐量变小。

GC很少,最大停顿时间变长,吞吐量增大。

CMS收集器(老年代)

CMS收集器的概念

n  CMS收集器

–      Concurrent Mark Sweep 并发[z2] 标记清除

–      标记-清除算法

–      与标记-压缩相比

–      并发阶段会降低吞吐量

–      老年代收集器(新生代使用ParNew

–      -XX:+UseConcMarkSweepGC

CMS收集器的运行过程

n  CMS运行过程比较复杂,着重实现了标记的过程,可分为

–      初始标记(用户线程暂停)

•       根可以直接关联到的对象

•       速度快

–      并发标记(和用户线程一起)

•       主要标记过程,标记全部可用对象

–      重新标记(用户线程暂停)

•       由于并发标记时,用户线程依然运行,因此在正式清理前,再做修正

–      并发清除(和用户线程一起)

•       基于标记结果,直接清理对象

工作示意图:

GC参数

GC日志:

1.662: [GC [1 CMS-initial-mark: 28122K(49152K)] 29959K(63936K), 0.0046877 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

1.666: [CMS-concurrent-mark-start]

1.699: [CMS-concurrent-mark: 0.033/0.033 secs] [Times: user=0.25 sys=0.00, real=0.03 secs]

1.699: [CMS-concurrent-preclean-start]

1.700: [CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

1.700: [GC[YG occupancy: 1837 K (14784 K)]1.700: [Rescan (parallel) , 0.0009330 secs]1.701: [weak refs processing, 0.0000180 secs] [1 CMS-remark: 28122K(49152K)] 29959K(63936K), 0.0010248 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

1.702: [CMS-concurrent-sweep-start]

1.739: [CMS-concurrent-sweep: 0.035/0.037 secs] [Times: user=0.11 sys=0.02, real=0.05 secs]

1.739: [CMS-concurrent-reset-start]

1.741: [CMS-concurrent-reset: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

特点

n  特点

–      尽可能降低停顿

–      会影响系统整体吞吐量和性能

•       比如,在用户线程运行过程中,分一半CPU去做GC,系统性能在GC阶段,反应速度就下降一半

–      清理不彻底

•       因为在清理阶段,用户线程还在运行,会产生新的垃圾,无法清理

–     因为和用户线程一起运行,不能在空间快满时再清理

•       -XX:CMSInitiatingOccupancyFraction设置触发GC的阈值(-XX:CMSInitiatingOccupancyFraction=70[z3] )

•       如果不幸内存预留空间不够,就会引起concurrent mode failure

concurrent mode failure异常日志及处理:

33.348: [Full GC 33.348: [CMS33.357: [CMS-concurrent-sweep: 0.035/0.036 secs] [Times: user=0.11 sys=0.03, real=0.03 secs]

 (concurrent mode failure): 47066K->39901K(49152K), 0.3896802 secs] 60771K->39901K(63936K), [CMS Perm : 22529K->22529K(32768K)], 0.3897989 secs] [Times: user=0.39 sys=0.00, real=0.39 secs]

解决办法:使用串行收集器作为后备

总结:

CMS做初始标记时,是会产生全局停顿的。重新标记也是独占CPU的,因此也会产生全局停顿。

解决CMS会引起concurrent model failure错误的方案就是,设置GC阈值,或使用串行收集器作为后备。

引起concurrent model failure错误的原因:当GC线程与用户线程一起并发做垃圾清理时,由于用户线程还在运行,这是会产生新的对象占用内存,如果预留的空间不足,这是就会产生concurrent model failure并发模式失败),解决的思路是,当并发清楚时,预留足够的空间。或者暂停用户的线程,串行做垃圾清除。

有关内存碎片:

n  有关碎片[z4] 

–      标记-清除和标记-压缩

GC参数

指示碎片整理和CMSGC线程数的参数:

n  -XX:+ UseCMSCompactAtFullCollection[z5]   (开启在Full GC[z6] 后,进行碎片整理)

–     整理过程是独占的,会引起停顿时间变长

n  -XX:CMSFullGCsBeforeCompaction

–     设置进行几次Full GC后,进行一次碎片整理(-XX:CMSFullGCsBeforeCompaction=1 

n  -XX:ParallelCMSThreads[z7] 

–     设定CMS的线程数量

CMS GC要决定是否在发生full GC时做压缩,会依赖几个条件。其中, 
第一种条件,UseCMSCompactAtFullCollection CMSFullGCsBeforeCompaction 是搭配使用的;前者目前默认就是true了,也就是关键在后者上。 
第二种条件是用户调用了System.gc(),而且DisableExplicitGC没有开启。 
第三种条件是young gen报告接下来如果做增量收集会失败;简单来说也就是young gen预计old gen没有足够空间来容纳下次young GC晋升的对象。 

上述三种条件的任意一种成立都会让CMS决定这次做full GC时要做压缩。 
(还有另一个参数,CMSCompactWhenClearAllSoftRefs,这个就先不说了,反正你没有配置它,而且默认也是true) 

CMSFullGCsBeforeCompaction 说的是,在上一次CMS并发GC执行过后,到底还要再执行多少次full GC才会做压缩。默认是0,也就是在默认配置下每次CMS GC顶不住了而要转入full GC的时候都会做压缩。 
把CMSFullGCsBeforeCompaction配置为10,就会让上面说的第一个条件变成每隔10次真正的full GC才做一次压缩(而不是每10次CMS并发GC就做一次压缩,目前VM里没有这样的参数)。这会使full GC更少做压缩,也就更容易使CMS的old gen受碎片化问题的困扰。 
本来这个参数就是用来配置降低full GC压缩的频率,以期减少某些full GC的暂停时间。CMS回退到full GC时用的算法是mark(标记)-sweep(清除)-compact(压缩),compaction(压缩)是可选的,不做的话碎片化会严重些但这次full GC的暂停时间会短些;这是个取舍。

GC参数整理

n  -XX:+UseSerialGC:在新生代和老年代使用串行收集器

n  -XX:SurvivorRatio:设置eden区大小和survivior区大小的比例

n  -XX:NewRatio:新生代和老年代的比

n  -XX:+UseParNewGC:在新生代使用并行收集器

n  -XX:+UseParallelGC :新生代使用并行回收收集器

n  -XX:+UseParallelOldGC:老年代使用并行回收收集器

n  -XX:ParallelGCThreads:设置用于垃圾回收的线程数

n  -XX:+UseConcMarkSweepGC:新生代使用并行收集器,老年代使用CMS+串行收集器

n  -XX:ParallelCMSThreads:设定CMS的线程数量

n  -XX:CMSInitiatingOccupancyFraction:设置CMS收集器在老年代空间被使用多少后触发

n  -XX:+UseCMSCompactAtFullCollection:设置CMS收集器在完成垃圾收集后是否要进行一次内存碎片的整理

n  -XX:CMSFullGCsBeforeCompaction:设定进行多少次CMS垃圾回收后,进行一次内存压缩

n  -XX:+CMSClassUnloadingEnabled:允许对类元数据进行回收

n  -XX:CMSInitiatingPermOccupancyFraction:当永久区占用率达到这一百分比时,启动CMS回收

n  -XX:UseCMSInitiatingOccupancyOnly:表示只在到达阀值的时候,才进行CMS回收

GC参数–Tomcat实例

环境

n  Tomcat 7

n  JSP 网站

n  测试网站吞吐量和延时

工具

n  JMeter

目的

n  让Tomcat有一个不错的吞吐量(Throughput

系统结构

GC参数

Jmeter

n  性能测试工具

n  建立10个线程,每个线程请求Tomcat 1000次 共10000次请求

GC参数

JDK6:使用32M堆处理请求

n  参数:

n  set CATALINA_OPTS=-server -Xloggc:gc.log -XX:+PrintGCDetails -Xmx32M -Xms32M -XX:+HeapDumpOnOutOfMemoryError -XX:+UseSerialGC -XX:PermSize=32M

GC参数

GC参数

JDK6:使用最大堆512M堆处理请求

n  参数:

n  set CATALINA_OPTS=-Xmx512m -XX:MaxPermSize=32M  -Xloggc:gc.log -XX:+PrintGCDetails

结果:FULL GC很少,基本上是Minor GC

GC参数

GC参数

JDK6:使用最大堆512M堆,最小堆64M堆处理请求

–      参数:

–     set CATALINA_OPTS=-Xmx512m -Xms64m -XX:MaxPermSize=32M  -Xloggc:gc.log -XX:+PrintGCDetails

–      结果 GC数量减少 大部分是Minor GC

GC参数

GC参数

JDK6:使用最大堆512M堆,最小堆64M堆,使用Parallel收集器+并行老年代,限制并行GC线程数为4

n  处理请求

–      参数:

–     set CATALINA_OPTS=-Xmx512m -Xms64m -XX:MaxPermSize=32M  -Xloggc:gc.log -XX:+PrintGCDetails -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:ParallelGCThreads=4

–      结果:GC压力原本不大,修改GC方式影响很小

GC参数

JDK 6,减小堆大小,使用Serial回收器

–     set CATALINA_OPTS=-Xmx40m -Xms40m -XX:MaxPermSize=32M  -Xloggc:gc.log -XX:+PrintGCDetails

–      减小堆大小,增加GC压力,使用Serial回收器

GC参数

JDK 6,减小堆大小,使用并行回收器

–     set CATALINA_OPTS=-Xmx40m -Xms40m -XX:MaxPermSize=32M  -Xloggc:gc.log -XX:+PrintGCDetails -XX:+UseParallelOldGC -XX:ParallelGCThreads=4

–      减小堆大小,增加GC压力,使用并行回收器

GC参数

JDK 6,减小堆大小,使用ParNew回收器

–      set CATALINA_OPTS=-Xmx40m -Xms40m -XX:MaxPermSize=32M  -Xloggc:gc.log -XX:+PrintGCDetails -XX:+UseParNewGC

–      减小堆大小,增加GC压力,使用ParNew回收器

GC参数

启动Tomcat 7,使用JDK6,不加任何参数启动测试

–      使用JDK6

–      不加任何参数启动测试

GC参数

启动Tomcat 7,使用JDK7,不加任何参数启动测试

–      使用JDK7

–      不加任何参数启动测试

GC参数

升级JDK可能会带来额外的性能提升!

不要忽视JDK的版本哦。

 

 [z1]吞吐量可以理解为我们的应用系统提供服务的量。比如是一个网站的话,吞吐量指的就是这个网站能处理的请求量。

 [z2]与用户线程一起执行

 [z3]使用cms作为垃圾回收,当内存使用70%后开始CMS收集。

 [z4]不连续的,间断的内存空间,当要为对象分配空间时,只能从这几个不连续的内存空间中去查找能放的下这个对象的内存空间,如果这个内存能放的下这个对象,但是还剩一点点空间,这一点点空间就浪费了,因为它存不下任何东西,不会被分配。

 [z5]在FULL GC的时候, 对年老代的压缩整理。

         CMS是不会移动内存的, 因此, 这个非常容易产生碎片, 导致内存不够用, 因此, 内存的压缩这个时候就会被启用。 增加这个参数是个好习惯。可能会影响性能,但是可以消除碎片

 [z6]有一点需要注意的是:CMS并发GC不是“full GC”。HotSpot VM里对concurrent collection和full collection有明确的区分。所有带有“FullCollection”字样的VM参数都是跟真正的full GC相关而跟CMS并发GC无关的。

 [z7]

标志-XX:ConcGCThreads=<value>(早期JVM版本也-XX:ParallelCMSThreads)定义并发CMS过程运行时的线程数。比如value=4意味着CMS周期的所有阶段都以4个线程来执行。尽管更多的线程会加快并发CMS过程,但其也会带来额外的同步开销。因此,对于特定的应用程序,应该通过测试来判断增加CMS线程数是否真的能够带来性能的提升。

 

如果还标志未设置,JVM会根据并行收集器中的-XX:ParallelGCThreads参数的值来计算出默认的并行CMS线程数。该公式是ConcGCThreads = (ParallelGCThreads + 3)/4。因此,对于CMS收集器, -XX:ParallelGCThreads标志不仅影响“stop-the-world”垃圾收集阶段,还影响并发阶段。

转载于:https://my.oschina.net/kangxi/blog/1823161