线上系统压力分析,系统负载倍增时,频繁GC

线上系统部署架构

4 核 8 GB 的机器,分配了 4GB 给 JVM,3G的内存给堆,新生代和老年代各 1.5 GB,元数据区是 256 MB,栈的内存空间是 1M,人脸库管理系统有几个定时器任务,设备人脸同步任务,设备时间同步任务,还有其他框架的后台线程,和jvm的后台线程,大概几十个线程;
jstat -gc 命令观察到,Eden 内存每秒 2M 速度在新增,600s 左右 Eden区才满,5分钟左右一次YoungGC,GC 系统停顿 350ms,每次 YoungGC 完大概 200~300kb 的存活对象存入 S 区,等到下次 GC,之前存活的 200~300kb 可以同步被回收,从而达到 Old GC 保持的次数一直在零次,此时的系统JVM垃圾回收器使用 NewPar+CMS 是没问题的,因为这是一个后台计算同步的系统,不需要直接面向用户,所以 5 分钟一次 GC,每次停顿 350 ms,对用户的影响也是无感知的

线上的 JVM 参数配置,基本可以满足

1
2
3
4
5
-Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:MetaspaceSize=256M 
-XX:MaxMetaspaceSize=256M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=5
-XX:PretenureSizeThreshold=1M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFaction=92 -XX:+UseCMSCompactAtFullCollection
-XX:CMSFullGCsBeforeCompaction=0

由于系统过于简单,Yong GC 发生的频繁一点对系统造成都不会造成太大的影响,但是随着系统功能的迭代和使用的用户越多,导致系统的复杂度和数据同步的数据量越来越大,这个系统的影响会有变化么

系统负载 10 倍,100 倍

当系统负载增加到 10 倍时,进入 Eden 的对象是 20m/s,一分钟左右 Eden 就满了,此时会触发 Young GC,Young GC 前会检查老年代的可用空间大小 > 历次年轻代GC升入老年代对象大小之和的平均大小,由于老年代的内存分配是 1.5G,成立老年代内存空间担保成立,此时直接 Young GC,之前存活的对象大小在 200~300kb 左右,系统负载增加了10倍,CPU资源和磁盘和网络带宽资源都是有限的,部分任务处理的速度会下降,Young GC 后存活的对象大小在 5MB 左右,每次 GC 系统停顿的时间是 500ms,得益于是计算型的服务系统,不需要面向用户,还是扛得住

当系统负载增加到 100 倍时,进入 Eden 的对象是 200m/s,6 秒钟左右 Eden 就满了,第一次 Young GC 内存空间担保是成功的,直接 Young GC,系统停顿 10s,存活的对象是 100M,此时的系统 CPU、磁盘、网络各方面的压力都会显著增加,处理任务的时间越来越慢,虽然 Survivor 区能够存放 100M 的对象大小,但是也存在突然某次存活的对象大于 Survivor 150MB,直接进入老年代;而且不要忽略了动态年龄判断规则,当 Survivor 区的一组对象的内存 > Survivor内存x50%,大于这组对象的最大年龄的对象直接进入老年代,可能有点绕,就是 1 + 2 + n > Survivor内存x50%,n 以上年龄的对象直接进入老年代,此时进入老年代的对象越来越多

如果评估每次 YoungGC 进入老年代的对象是 50 MB,3 分钟左右一次 Old GC,老年代的对象存活率是远远高于新生代的,并且老年代用是不是复制算法,是标记整理算法,设计到初始标记-并发标记-重新标记-并发清理,内存整理多个步骤,Old GC 导致系统停止的时间是 Young GC 的 10几倍,6 秒一次 Young GC,3 分钟一次 Old GC,由于 CPU 和内存资源压力非常大,系统宕机的可能性也是非常高,同时系统由于高负载,比较属于不停卡顿的状态,导致大量的任务阻塞,尽可能的减少 Old GC,尽可能的合理分配内存空间,预估系统压力是非常重要的一件事

下面是老年代 CMS 垃圾回收工作时的几个状态

G1垃圾回收器规则

一旦新生代达到了设定的占据堆内存的最大大小 60%,此时就会触发GC,和之前不一样的是,会根据预设的系统停顿时间来评估各个 region,有最短的垃圾回收时间尽可能的回收更多的垃圾,还是采用的是复制算法
(1)对象在新生代躲过了很多次的垃圾回收,达到了一定的年龄了,“-XX:MaxTenuringThreshold” 参数可以设置这个年龄,他就会进入老年代

(2)动态年龄判定规则,如果一旦发现某次新生代GC过后,存活对象超过了Survivor 的 50%

老年代的region内存大小占到堆内存的 45% 的时候,触发混合GC,混合GC是新生代和老年代一起回收,首先是初始标记-并发标记-最终标记,混合回收阶段,初始标记和最终标记都需要STW,为了保证系统停顿的时间不超过 MaxPauseMillis,需要进行多次最后阶段的混合GC,-XX:G1MixedGCCountTarget=8,如果设置了如下参数 -XX:G1HeapWastePercent=5%,表示当回收过程中空闲的Region占内存大小比例的 5% 时,就停止回收,也不需要内存整理,-XX:G1MixedGCLiveThresholdPercent,这个参数是表示在回收过程中如果Region中存活的对象占比是低于 85%,才进行回收,由于基于G1垃圾回收整体是采用复制算法,不太容易产生内存碎片,