携手创作,共同成长!这是我参与「日新计划 8 月更文挑战」的第12天,点击查看活动详情
1.写在前面
在上一篇文章中,我们分享了jvm调优中:堆内存与元空间优化,新生代和老年代比例优化,和线程堆栈优化。
具体的详情可以查看:JVM调优-JVM调优实践二
通过了这个三个方面的内容,对jvm进行相应的调优实践,并得到最终的结论。
好了,那我们想象一下,我们还可以往哪个方向去调优呢?
嘿,我们之前花了比较大的时间分析了jvm的垃圾回收器,那我们是不是可以从jvm垃圾回收器方向进行调优呢?
答案是肯定的,那我们今天就继续,接下来的jvm调优实践吧!!!
那我们今天就继续往下进行分析:
- 垃圾回收器优化(吞吐量,响应时间)
那就废话不多说了,直接上正菜吧:
2.垃圾回收器优化:吞吐量优先ps+po
- Perallel-Scavenge垃圾收集器:
-XX:+UsePerallelGC
- Perallel-Old垃圾收集器【po是ps的老年代并行收集版本】:
-XX:+UsePerallelOldGC
- 吞吐量优先
- 老年代采用并行垃圾回收
默认使用 ps+po
垃圾回收器组合: 并行垃圾回收器组合
这里说的垃圾收集器的搭配,我们之前已经分享过了,估计大家伙都忘了吧,这里,我们再回顾一下:
纳,就是上面这个喽:(答应我,别再忘了)
优化后的参数配置:
JAVA_OPT="${JAVA_OPT} -Xms424m -Xmx424m -XX:MetaspaceSize=128m -Xss512k"
JAVA_OPT="${JAVA_OPT} -XX:+UseParallelGC -XX:+UseParallelOldGC "
JAVA_OPT="${JAVA_OPT} -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:E:/logs/gc-ps-po.log"
idea配置:
-Xms424m -Xmx424m -Xss512k -XX:MetaspaceSize=128m -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:E:/logs/gc-default.log
然后,重启一下项目。
这里,我们用jmeter,进行压力测试,看看平均响应时间:
平均响应时间: 1904
这里,jmeter如何进行压力测试,大家伙可以往前看,我的文章有说明。
这里就不再展开描述了,直接给出测试结果了。
3.垃圾回收器优化:响应时间优先parnew+cms
- ParNew垃圾收集器【Serial的收集器的多线程版本】:
-XX:UseParNewGC
- 年轻代是并行的垃圾回收,老年代是串行垃圾回收
- 注意:单核的CPU性能并不如Serial,没有线程切换成本
- CMS垃圾收集器: 并发 收集器(非独占式)
-XX:ConcMarkSweepGC
【并发的标记清楚算法的GC垃圾收集器】- 响应优先(低延时)
- 可以执行并发收集的垃圾收集器
- 垃圾收集采用的是标记清除算法
- 缺点:容易产生内存碎片,对CPU比较敏感
- 主要分为四个阶段:
- 初始标记(Initial-Mark)阶段 :【STW】
- 并发标记(Concurrent-Mark)阶段 :
- 重新标记(Remark)阶段 :【STW】
- 并发清除(Concurrent-Sweep)阶段 :
使用cms垃圾回收器,垃圾回收器组合: parNew+CMS, cms垃圾回收器在垃圾标记,垃圾清除的时候,和业务线程交叉执行,尽量减少stw时间,因此这垃圾回收器叫做响应时间优先
;
纳,就是上面这个喽:(答应我,别再忘了)
优化后的参数配置:
JAVA_OPT="${JAVA_OPT} -Xms424m -Xmx424m -XX:MetaspaceSize=128m -Xss512k"
JAVA_OPT="${JAVA_OPT} -XX:+UseParNewGC -XX:+UseConcMarkSweepGC "
JAVA_OPT="${JAVA_OPT} -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:E:/logs/gc-parnew-cms.log"
idea配置:
-Xms424m -Xmx424m -Xss512k -XX:MetaspaceSize=128m -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:E:/logs/gc-default.log
然后,重启一下项目。
这里,我们用jmeter,进行压力测试,看看平均响应时间:
平均响应时间: 1893
由上可见,这两种方式,平均响应时间,其实,相差不大。
这里,jmeter如何进行压力测试,大家伙可以往前看,我的文章有说明。
这里就不再展开描述了,直接给出测试结果了。
4.G1垃圾收集器(重点)
Garbage First(简称G1) 收集器是垃圾收集器技术发展历史上的里程碑式的成果
JDK 8以后G1收集器才被Oracle官方称为“全功能的垃圾收集器”(Fully-Featured Garbage Collector)。
G1是一款主要面向服务端应用的垃圾收集器。
JDK 9发布之日,G1宣告取代Parallel Scavenge加Parallel Old组合,成为服务端模式下的默认垃圾收集器,而CMS则沦落至被声明为不推荐使用(Deprecate)的收集器。
由此可见,G1变得越来越重要了。CMS变成了中间过度。
G1中提供了三种模式垃圾回收模式, Young GC、Mixed GC 和 Full GC ,在不同的条件下被触发。
G1的使用步骤:
- 配置开启G1
- 设置对的最大内存
- 设置GC最大暂停时间:设置100-300之间是比较合理:
- 如果设置的GC的暂停时间比较小,系统吞吐量会降低
- 默认的值是200,
- G1设计的目标:控制用户线程执行时间90%,GC占比时间10%
4.1原理
G1中没有之前所谓老年代,新生代,Eden、S0、S1!!!!
G1垃圾收集器相对比其他收集器而言,最大的区别在于它取消了 年轻代、老年代的物理划分, 取而代之的是将堆划分为若干个区域(Region),这些区域中包含了有逻辑上的年轻代、老年代区域。
这样做的好处就是,我们再也不用单独的空间对每个代进行设置了,不用担心每个代内存是否足够。
这个确实是一个大很大的优点。
在G1划分的区域中,年轻代的垃圾收集依然采用暂停所有应用线程的方式,将存活对象拷贝到老年代或者Survivor空间,G1收集器通过将对象从一个区域复制到另外一个区域,完成了清理工作。
这就意味着,在正常的处理过程中,G1完成了堆的压缩(至少是部分堆的压缩),这样也就不会有cms内存碎片问题的存在了。
4.2 Young GC
Young GC主要是对Eden区进行GC,它在Eden空间耗尽时会被触发。
Remembered Set(记忆集合)
- 如何找到垃圾?引用计数法&根可达算法GC Roots
基于根可达算法回收垃圾对象,判断对象是否是GC Roots,如果全量扫描老年代,那么这样扫描下来会耗费大量的时间。
于是,G1引进了RSet的概念。它的全称是Remembered Set,其作用是用来记录并跟踪其它Region指向该Region中对象的引用。
RSet 与 Card 的关系
每个Region初始化时,会初始化一个RSet,该集合用来记录并跟踪其它Region指向该Region中对象的引用,每个Region默认按照512Kb划分成多个Card,所以RSet需要记录的东西应该是 xx Region的 xx Card。
4.3 Mixed GC
特点: 回收年轻代内存,同时也回收部分老年代的内存
当越来越多的对象晋升到老年代old region时,为了避免堆内存被耗尽,虚拟机会触发一个混合的垃圾收集器,即Mixed GC,该算法并不是一个Old GC,除了回收整个Young Region,还会回收一部分的Old Region
注意:是一部分老年代,而不是全部老年代,可以选择哪些old region进行收集,从而可以对垃圾回收的耗时时间进行控制。也要注意的是Mixed GC 并不是 Full GC。
MixedGC什么时候触发?
-XX:InitiatingHeapOccupancyPercent=n
,设置触发标记周期的 Java 堆占用率阈值。默认占用率是整个 Java 堆的 45%。
MixedGC主要分为两步:
- 全局并发标记
- 拷贝存活对象
好了,相关的原理,描述得比较多,大家耐心点看喽。
接下来,就开启配置。
4.4 G1收集器相关参数
# 使用 G1 垃圾收集器
-XX:+UseG1GC
# 设置期望达到的最大GC停顿时间指标(JVM会尽力实现,但不保证达到),默认值是 200 毫秒。
-XX:MaxGCPauseMillis=
# 设置的 G1 区域的大小。值是 2 的幂,范围是 1 MB 到 32 MB 之间。目标是根据最小的 Java 堆大小划分出约 2048 个区域。
# 默认是堆内存的1/2000。
-XX:G1HeapRegionSize=n
# 设置 STW 工作线程数的值。将 n 的值设置为逻辑处理器的数量。n 的值与逻辑处理器的数量相同,最多为 8。
-XX:ParallelGCThreads=n
# 设置并行标记的线程数。将 n 设置为并行垃圾回收线程数 (ParallelGCThreads)的 1/4 左右。
-XX:ConcGCThreads=n
# 设置触发标记周期的 Java 堆占用率阈值。默认占用率是整个 Java 堆的 45%。
-XX:InitiatingHeapOccupancyPercent=n
4.5 测试
JAVA_OPT="${JAVA_OPT} -Xms424m -Xmx424m -XX:MetaspaceSize=128m -Xss512k"
JAVA_OPT="${JAVA_OPT} -XX:+UseG1GC -XX:MaxGCPauseMillis=100"
# MaxGCPauseMillis设置的值建议是100-300之间
JAVA_OPT="${JAVA_OPT} -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:E:/logs/gc-g-one.log"
idea配置:
-Xms424m -Xmx424m -Xss512k -XX:MetaspaceSize=128m -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:E:/logs/gc-default.log
然后,重启一下项目。
看看效果:
-
GC统计:
-
gc原因
由上可见,可以看到gc的次数,明显减少。暂停时间也有减少。
由此可见,我们这里,已经是对垃圾回收器优化,进行了相关的描述了。
那到此,jvm的调优就到此告一段落了。多谢大家的点赞,评论,收藏,三连!!!
好了,以上就是JVM调优实践三的分享了。
个人理解,可能也不够全面,班门弄斧了。
如果觉得有收获的,帮忙点赞、评论、收藏
一下呗!!!