作者:京东零售刘乐1.ParallelGCThreads参数含义在说这个参数之前,先说说JVM垃圾回收(GC)算法的两个优化目标:吞吐量和暂停持续时间。JVM将使用特定的GC收集线程。当GC开始时,GC线程会与业务线程抢占CPU时间。吞吐量定义为CPU在业务线程上花费的时间与总CPU时间的比率。为了承接更大的流量,吞吐量越高越好。为了安全的垃圾回收,在GC或者GC的某个阶段,所有的业务线程都会被挂起,也就是STW(StopTheWorld)。STW的持续时间就是停顿的时长,影响响应速度,所以越小越好。这两个优化目标是冲突的。在一定范围内,参与GC的线程越多,暂停时间越短,但吞吐量越小。在生产实践中,需要根据业务特点设置合理的GC线程数,以达到吞吐量和停顿时间的平衡。目前广泛使用的GC算法,包括PSMarkSweep/PSScavenge、ConcurrentMarkSweep/ParNew、G1等,都可以使用ParallelGCThreads参数来指定JVM在并行GC时参与垃圾回收的线程数。设置太小会导致GC停顿时间变长影响RT,设置太大会影响吞吐量,导致CPU高。2、ParallelGCThreads参数设置GC并发线程数可以通过JVM启动参数指定:-XX:ParallelGCThreads=。如果不指定,JVM会根据逻辑核心数ncpus使用以下公式计算默认值:?当ncpus小于8时,ParallelGCThreads=ncpus?否则,ParallelGCThreads=8+(ncpus-8)(5/8)一般来说,在没有特殊要求的情况下,ParallelGCThreads参数可以使用默认值。但在JRE1.8.0_131版本之前,JVM无法感知Docker的CPU限制,会使用宿主机的逻辑核心数来计算默认值。例如部署在128核物理机上的容器,JVM中默认的ParallelGCThreads为83,远远超过了容器的核数。过多的GC线程占用了业务线程的CPU时间,线程切换的开销大大降低了吞吐量。因此,在JRE1.8.0_131之前的版本中,如果没有明确指定ParallelGCThreads,会存在较大的风险。3.ParallelGCThreads参数实验创建8C12G容器,宿主机为128C。模拟真实在线流量,使用相同的QPS,观察对比JVMYoungGC、JVMCPU、容器CPU等监控数据。场景如下:?场景一:JVMParallelGCThreads默认值,QPS=420,持续5分钟,CPU恒定在70%?场景二:JVMParallelGCThreads=8,QPS=420,持续5分钟,CPU恒定在65%?场景3:JVMParallelGCThreads默认值,瞬时QPS压力420,前1minCPU持续100%。场景四:JVMParallelGCThreads=8,瞬间QPS压力为420,前2sCPU持续100%,之后从监控数据回落。各场景CPU差距明显,尤其是场景3和场景4的对比。场景3:由于GC线程过多,CPU占用100%长达1分钟。可以得出以下两个结论:1、修改ParallelGCThreads=8后,在相同QPS下CPU会降低5%左右。2、修改ParallelGCThreads=8后,在瞬时压力大,CPU占满的情况下,CPU会恢复得更快。1:容器CPU对比图:场景3(上)和场景4(下)图2:JVMYoungGC对比图:场景3(上)和场景4(下)4.ParallelGCThreads修改建议ParallelGCThreads配置有风险的应用,修改方法是以下两个选项(二选一):?将JRE版本升级到1.8.0_131或以上,推荐1.8.0_192?在JVM启动参数中指定-XX:ParallelGCThreads=,N为推荐下表取值:容器核心数248163264推荐值248132343推荐上下界1~22~44~88~1616~3232~64