JVM调优之垃圾回收器选择

1、概述:

对于JVM的垃圾回收器影响因素较多,例如:IO、临时变量、常驻对象、对象大小、CPU等各种软硬件配置,需要满足的场景也存在差异,例如:吞吐量量优先、响应耗时优先,以及2者的平衡;所以以下是从促销各个系统之前调优历程的一些总结,作为后续的参考方向。由于虚拟机比较多,以下描述主要是基于Hotspot。

2、背景描述:

促销主要核心系统之前部分接口存在耗时较大,经常触发到报警阈值,进行了一系列调参;主要参考指标:接口耗时、YGC耗时、YGC次数、FGC耗时、FGC频繁程度(次数);促销核心系统JVM垃圾回收器选择经历了这么几个阶段:

  ParNew + CMS----->G1----->ParNew + CMS----->PS+PO(当前线上)

3、ParNew + CMS :

  适用场景:重视服务器响应速度,要求系统停顿时间最短。
  此阶段促销系统是环境是:JDK7 + 2核4G
ParNew :

1、是Serial收集器的多线程版本,也使用复制算法
2、垃圾收集过程中同样也要暂停所有其他的工作线程(缺点)
3、默认开启和CPU数目相同的线程数
4、在Server模式下新生代的默认垃圾收集器

CMS :
特点:

1、是一种年老代垃圾收集器
2、最主要目标是获取最短垃圾回收停顿时间
3、是Sun HotSpot虚拟机中第一款真正意义上并发垃圾收集器,它第一次实现了让垃圾收集线程和用户线程同时工作
4、最短的垃圾收集停顿时间可以为交互比较高的程序提高用户体验,高速响应
5、使用多线程的标记-清除算法

缺点:

1、对CPU资源敏感,在多CPU下才能体现优势;虽然不会导致用户线程停顿,但是占用CPU导致应用程序变慢,引起吞吐量下降;
2、永久代空间(或JDK8的元空间)耗尽,默认情况下,CMS不会对永久代进行收集,一旦永久代空间耗尽,就回触发Full GC;
3、无法处理浮动垃圾,CMS在并发清理阶段用户线程依然在运行,并产生新垃圾(浮动垃圾),本次无法清除,需待下次GC清除;CMS的预留空间(-XX:CMSInitiatingOccupancyFraction设置预留空间占比)不够用户线程使用,会出现“Concurrent Mode Failure”失败,此时JVM会临时启用Serial Old收集器来重新进行老年代的垃圾收集,导致停顿时间变长,性能反而降低;
4、会产生大量内存空间碎片,大量内存碎片可能实际老年代空间很多,但不够大块对象使用,导致提前触发FGC;虽然CMS提供了碎片空间整理压缩机制,但是整理过程不是并发,导致停顿变长;

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

但是有个明显的弊端,就是当堆空间持续增大时,垃圾回收的时间也将会相应的持续增大,对应应用暂停的时间也会相应的增大。一些对相应时间要求很高的应用,比如最大暂停时间要求是几百毫秒,那么当堆空间大于几个G时,就很有可能超过这个限制,在这种情况下,垃圾回收将会成为系统运行的一个瓶颈。为解决这种矛盾,有了并发垃圾回收算法,使用这种算法,垃圾回收线程与程序运行线程同时运行。在这种方式下,解决了暂停的问题,但是因为需要在新生成对象的同时又要回收对象,算法复杂性会大大增加,系统的处理能力也会相应降低,同时,“碎片”问题将会比较难解决。所以,对于高并发系统,CMS也并非最优。

4、G1 :

 适用场景:要求尽可能可控 GC 停顿时间;内存占用较大的应用;
 
 此阶段促销系统是环境是:JDK7 + 2核4G;
 G1需要在多CPU、多核的情况下才能体现出优势,加之促销当时是JDK7,所以效果并不明显,还没有ParNew + CMS效果好。
 总体来说G1是一款厉害的垃圾回收器,不过要搭配上对应的环境;
 G1在低停顿优势很明显,但是系统追求高吞吐量,G1效果并不一定明显。
特点:

1、JDK7引入,JDK8及以上成熟,JDK9默认收集器;
2、基于标记-整理算法,不产生内存碎片;
3、可以非常精确控制停顿时间,在不牺牲吐量前提下,实现低停顿垃圾回收;
4、G1收集器避免全区域垃圾收集,它把堆内存划分为大小固定的几个独立区域,并且跟踪这些区域的垃圾收集进度,同时在后台维护一个优先级列表,每次根据所允许的收集时间,优先回收垃圾最多的区域;

缺点:

1、G1内部有个region区域块的概念, 它的大小和大对象很难保证一致,这会导致空间的浪费;特别大的对象是可能占用超过一个 region 的。如果设置region大小不合理,会导致大对象分配空间时内存空间地址不连续。

5、Parallel Scavenge+Parallel Old:

Parallel Scavenge
优点:

1、是一个新生代垃圾收集器,同样使用复制算法
2、是一个多线程的垃圾收集器
3、它重点关注的是程序达到一个可控制的吞吐量
4、主要适用于在后台运算而不需要太多交互的任务
5、自适应调节策略也是ParallelScavenge收集器与ParNew收集器的一个重要区别

缺点:

1、回收的时间变短了,但是回收的次数会增多,这一点在促销系统实践上非常明显。

Parallel Old:
优点

1、是Parallel Scavenge的年老代版本
2、使用多线程的标记-整理算法
3、JDK7、JDK8 默认使用该收集器作为老年代收集器

缺点:

1、停顿时间长

应用场景:在Server模式,多CPU的情况下,如果系统对吞吐量要求比较高,可以优先考虑新生代Parallel Scavenge和年老代Parallel Old收集器的搭配策略。
此阶段促销系统是环境是:(J-one下)JDK7 + 2核4G;(Jdos下)JDK8 + 2核4G。
此阶段一个比较明显的效果是YGC的时间缩短了,FGC的频繁程度降低了;
对于此搭配一个心得是需要结合NewRatio与SurvivorRatio等参数来调试,包括合理的Eden、From、To空间的设置才会有较优的效果。

6、促销系统效果:

YGC时间变短了,之前有出现过80ms的情况,现在基本是在8ms~50ms之间;FGC的频率明显降低,甚至基本不发生FGC。

7、总结:

虽然以上是针对垃圾回收器的特点介绍和选择,但促销实际过程中还结合了很多其他JVM参数配套调试,所以要达到一个比较良好的效果需要结合系统配置、环境、代码等因素综合考虑;建议在压测时候进行参数调试。
平衡取舍,如果系统不关心耗时,那么内存允许的条件下直接开大内存搞,如果系统对耗时极为苛刻,那么JVM调优也只是一方面,有条件扩容还需扩容,以及升级配置。
(典型例子:主站核心系统主要使用堆起高配机子策略,类似JVM细节配置反而次之)

你可能感兴趣的:(JAVA,jvm,java)