JVM调优之垃圾回收器选择

1、概述:

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

2、背景描述:

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

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

3、对于ParNew + CMS:

       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也并非最优。
                     此阶段营销系统是环境是:JDK7 + 2核4G

4、对于G1:

       特点:
                     1、JDK7引入,JDK8及以上成熟,JDK9默认收集器;
                     2、基于标记-整理算法,不产生内存碎片;
                     3、可以非常精确控制停顿时间,在不牺牲吐量前提下,实现低停顿垃圾回收;
                     4、G1收集器避免全区域垃圾收集,它把堆内存划分为大小固定的几个独立区域,并且跟踪这些区域的垃圾收集进度,同时在后台维护一个优先级列表,每次根据所允许的收集时间,优先回收垃圾最多的区域;
       缺点:
                     1、G1内部有个region区域块的概念, 它的大小和大对象很难保证一致,这会导致空间的浪费;特别大的对象是可能占用超过一个 region 的。如果设置region大小不合理,会导致大对象分配空间时内存空间地址不连续。

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

5、对于PS:

       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细节配置反而次之)。

 

posted @ 2020-09-17 21:13  晓等  阅读(1510)  评论(0编辑  收藏  举报