您的位置:首页 > 其它

第五课:GC参数

2020-02-02 20:39 99 查看

1.堆的回顾:

在一些情况下,新生对象不是分配在eden区的,也可进行栈上分配,也可能是一些大对象,直接进入了老年代。
S0 S1 大小相等,功能也相似,采取复制算法.

2.串行收集器

GC参数-串行收集器

GC参数-串行收集器
·最古老
·效率高,最稳定
·可能会产生较长的挺短(只是一个线程),在多核的情况下没法发挥很好的性能
·-xx +UserSerialGC
--新生代, 来年代使用串行回收
--新生复制算法
--老年代标记-压缩
如图一所示:full即老年代
串行收集器:
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]

3.并行收集器

·ParNew
- +UseParNewGc
·新生代并行
·老年代串行
-复制算法
-多线程,需要多核支持
- ParallelGCThreads 限制线程数量
如图二所示:看到ParNew就知道了,这个里面,在新生代使用的是并行收集器
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]
但是注意一点:多线程,他不一定比块。因为在你使用穿行收集器的时候,也许配置只是一个单核cpu,无法支持多核线程。
第二种并行收集器(穿行收集器在新生代和老年代的一个并行化)

Parallel收集器
-类似ParNew
-新生代复制算法
-老年代 标记-压缩
-更加关注吞吐量
- +UserParallelGC
·使用Parallel收集器+老年代串行
- +UserParallelOldGC
·使用Parallel收集器+并行老年代
图例和2一样:
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]
psYoungGen:年轻代并行化
PsPERMGEN:老年代并行化

另外两个重要的参数

- MaxGCPauseMiles
-- 最大停顿时间,单位毫秒
-- Gc努力保证回收时间爆炒过设定值
- GCTimeRation
-- 0-100的取值范围
-- 垃圾时间时间占总时间的比
-- 默认99 ,即最大允许1%的时间做gc
但是这两个参数是互相矛盾的。
因为,这样来理解,gc的总量是一定的,但是我可以安排gc什么时候去工作。如果我把gc频率提高,那么单次时间减少,
但总性能却降低。因此	 要尽可能的减少gc的发生,GCTimeRation这个参数也就变大了,因此每一次gc都会面向很多对象和垃圾,
但是单次gc时间时间却变长。而在第一个参数当中,设置的时候却尽可能的减少gc时间,所以这两个是互相矛盾的。
或者这么想。这里有一个吞吐量的概念,如果吞吐量大,那么在运行时,收到的回应就越多,那么时间就越短。
但是吞吐量,却是要占用cpu时间的,如果我把cpu时间分配给系统的吞吐量之上,那么自然分配给gc的时间就要少,但是gc
的总时间还是一定的,垃圾就在那里。也就因此矛盾。

这两个参数是矛盾的。因为停顿时间和吞吐量不可能同时被优化。

4.cms收集器

- Concurrent Mark Sweep 并发标记清除
- 标记清除算法
- 并发和并行的区别
- 并发阶段会降低吞吐量
- 老年代收集器(新生代是有parnew)
- +UserConcmarkSweepGC
运行过程很复杂,这里赵卓实现了标记的过程,可分为:
- 初始标记
·根可以直接关联到的对象
·速度快
- 并发标记(和线程一起执行)
·主要标记过程,标记全部对象,扫描全部对象
- 重新标记(独占cpu,停顿)
·由于并发表标记时,用户线程依然运行,因此在正式清理前,再做修正
-并发清理(和线程一起执行)
· 基于标记结果,直接清理对象

如图三所示:在这个过程当中,首先比较所有可达对象。然后并发标记,扫描全部对象,进行标记。因为并发标记是并发的,线程还在运行,所以有一些对象还在新产生。因此还需要一个重新标记,全局停顿,重新标记。最后并发清除。(为什么不是并发压缩:因为是并发过程,压缩之时很难让程序继续执行,这个时候就使用标记清除最简便)

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-concurre因为在清理阶段,nt-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] 下一次标记做准备 ,内部的数据年结构,对象啊做一个清理和重置

特点:

-尽可能降低停顿
-会影响系统整体吞吐量和性能
·比如,比如,在用户线程过程中,分去一半的cpu去做gc,系统性能在gc阶段,反应速度就下降一半
-清理不彻底
·因为在清理阶段,还在并行清理,线程还在跑,会产生新的垃圾,无法清理
-因为和用户线程一起清理,不能在空间快满时再清理
·CMSInitiatingOccupancyFraction设置触发GC的阈值【当这个堆空间占用到百分之多少,自动触发gc】
·如果不幸内存空间预留不足,就会引起concurrennt mode failure(因为是和用户线程一起执行的,所以在清理的时候,程序也会申请空间,而但当申请的空间不足,就会引起concurrennt mode failure这个错误)

concurrennt 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]

碎片:

-标记清除和标记压缩有什么不一样
标记清除可能会有一些碎片产生。标记压缩就不会。比如申请5个单元的连续空间,标记清除也许是分不出来的。标记压缩就可以。
明显的,标记压缩在性能上好于标记清除。那为什么在cms算法当中运用的是标记清除算法呢?因为cms更关注的是stw,
停顿时长,如果清理完再去移动存活对象,和应用程序并发执行,应用程序就有可能找不到对象在哪里。
几个参数::::-XX:+ UseCMSCompactAtFullCollection Full GC后,进行一次整理
整理过程是独占的,会引起停顿时间变长
几个参数::::-XX:+CMSFullGCsBeforeCompaction
设置进行几次Full GC后,进行一次碎片整理
(实际上cms只是在某一阶段减少了stw的时间,但随着应用线程一直在产生垃圾,所以这些垃圾一直都在,并没有从根本上解决问题,到最后还是要进行一个能够引起长时间gc的stw)
几个参数::::-XX:ParallelCMSThreads(约等于剩余可用的cpu数量,尽量不要设置的太大)
设定CMS的线程数量

为了减轻gc压力,我们应该注意什么:1.软件如何架构 2.代码如何写 3.堆空间如何分配

一些常用的参数:

-XX:+UseSerialGC:在新生代和老年代使用串行收集器
-XX:SurvivorRatio:设置eden区大小和survivior区大小的比例
-XX:NewRatio:新生代和老年代的比
-XX:+UseParNewGC:在新生代使用并行收集器
-XX:+UseParallelGC :新生代使用并行回收收集器
-XX:+UseParallelOldGC:老年代使用并行回收收集器
-XX:ParallelGCThreads:设置用于垃圾回收的线程数
-XX:+UseConcMarkSweepGC:新生代使用并行收集器,老年代使用CMS+串行收集器
-XX:ParallelCMSThreads:设定CMS的线程数量
-XX:CMSInitiatingOccupancyFraction:设置CMS收集器在老年代空间被使用多少后触发
-XX:+UseCMSCompactAtFullCollection:设置CMS收集器在完成垃圾收集后是否要进行一次内存碎片的整理
-XX:CMSFullGCsBeforeCompaction:设定进行多少次CMS垃圾回收后,进行一次内存压缩
-XX:+CMSClassUnloadingEnabled:允许对类元数据进行回收
-XX:CMSInitiatingPermOccupancyFraction:当永久区占用率达到这一百分比时,启动CMS回收
-XX:UseCMSInitiatingOccupancyOnly:表示只在到达阀值的时候,才进行CMS回收

5.Tomcat实例演示

-环境
-Tomcat 7
-JSP 网站
-测试网站吞吐量和延时
-工具
-JMeter(APACHE)
-目的
-让Tomcat有一个不错的吞吐量

系统结构

tomcat(服务器) -------- Jmeter(计算机)    不放在一台计算机上面,防止Jmeter对Tomcat的运行产生影响
局域网连接

Jmeter

性能测试工具
建立10个线程,每个线程请求Tomcat 1000次 共10000次请求

JDK6:使用32M堆处理请求

参数:
set CATALINA_OPTS=-server -Xloggc:gc.log -XX:+PrintGCDetails -Xmx32M -Xms32M -        XX:+HeapDumpOnOutOfMemoryError -XX:+UseSerialGC(古老的串行回收器) -XX:PermSize=32M
Average:6 media4 90%:7 min:2 max:135 error:0.00% Throughput:540.6/sec  kb/sec:4127.8
5次gc,大量的请求在很短的时间内2秒内有4次gc的时间产生,每次gc0.08s

JDK6:最简单的方法就是使用最大堆512M堆处理请求

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

结果:FULL GC很少,基本上是Minor GC
Throughput:651/sec
从开始运行之时,系统只有16M,随着运行内存在往上涨,达到60M。整个过程没有full gc的产生。因此产生了比较好的效果,因为fullgc是比较花时间的。

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

参数:
set CATALINA_OPTS=-Xmx512m -Xms64m -XX:MaxPermSize=32M  -Xloggc:gc.log -XX:+PrintGCDetails(上面的因为堆在    扩展,但是我这里直接设置到64,因为系统会尽力维持在小堆上面进行运作,所以会花费多次gc。我直接把堆给增大,就不需要  那么多次gc了,所以性能就变好了)
Throughput:674/secs
结果 GC数量减少 大部分是Minor GC

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

参数:
set CATALINA_OPTS=-Xmx512m -Xms64m -XX:MaxPermSize=32M  -Xloggc:gc.log -XX:+PrintGCDetails -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:ParallelGCThreads=4
(都使用并行回收期,并设置4个线程来运行)
结果:GC压力原本不大,修改GC方式影响很小
Throughput:669/secs

JDK 6

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

减小堆大小,增加GC压力,使用Serial回收器
Throughput:646/secs

JDK 6

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

减小堆大小,增加GC压力,使用并行回收器
Throughput:685/secs

JDK 6

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

减小堆大小,增加GC压力,使用ParNew回收器
gc的大头是老年代,所以把年轻代设置成aprnew,意义并不大

启动Tomcat 7

使用JDK6
不加任何参数启动测试
Throughput:622.5/secs

使用JDK7
不加任何参数启动测试
Throughput:680/secs

不要忽略jdk的版本

性能的根本在应用

gc参数属于微调

设置不合理,会影响性能,产生大的延时
  • 点赞
  • 收藏
  • 分享
  • 文章举报
睡觉不磨牙的蜗牛 发布了14 篇原创文章 · 获赞 0 · 访问量 313 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: