# G1CG调优 ## 建议 ### 宿主机G1配置 ```shell java -jar -Xmx4096m -Xms4096m -XX:+UseG1GC -XX:MaxGCPauseMillis=50 -XX:G1ReservePercent=10 -XX:ConcGCThreads=2 -XX:ParallelGCThreads=5 -XX:G1HeapRegionSize=2m -XX:MaxTenuringThreshold=14 -XX:SurvivorRatio=8 -XX:G1HeapRegionSize=1M -XX:InitiatingHeapOccupancyPercent=40 -XX:+UseStringDeduplication ``` - -XX:G1HeapRegionSize=2m: Region的大小配置,可以增大Region大小配置来减少大对象占用的Region数 - -XX:MaxGCPauseMillis=50: GC停顿时间 - -XX:InitiatingHeapOccupancyPercent=40: 老年代Region触发混合GC的占比,默认值是45,也就是说老年代占据了堆内存45%的Region的时,会触发混合GC。该值一般不需要调整,这样可以让JVM内存占用维持在50%左右 - -XX:+UseStringDeduplication: 开启字符串去重 G1回收器生效 ### 容器G1配置 ```shell java -jar -XX:+UseContainerSupport -XX:InitialRAMPercentage=70.0 -XX:MaxRAMPercentage=70.0 -XX:+UseG1GC -XX:MaxGCPauseMillis=50 -XX:G1ReservePercent=10 -XX:ConcGCThreads=2 -XX:ParallelGCThreads=5 -XX:G1HeapRegionSize=2m -XX:MaxTenuringThreshold=14 -XX:SurvivorRatio=8 -XX:G1HeapRegionSize=1M -XX:InitiatingHeapOccupancyPercent=40 -XX:+UseStringDeduplication ``` ### 宿主机ZGC配置 ```shell java -jar --add-opens=java.base/java.lang=ALL-UNNAMED -Xms4096m -Xmx4096m -XX:ReservedCodeCacheSize=256m -XX:InitialCodeCacheSize=256m -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -XX:ConcGCThreads=1 -XX:ParallelGCThreads=3 -XX:ZCollectionInterval=60 -XX:ZAllocationSpikeTolerance=4 -XX:+UnlockDiagnosticVMOptions -XX:-ZProactive -Xlog:safepoint,classhisto*=trace,age*,gc*=info:file=/opt/gc-%t.log:time,tid,tags:filecount=5,filesize=50m ``` ## 参考 - https://cloud.tencent.com/developer/article/1518229 - -XX:+UseG1GC -XX:+UseStringDeduplication - -Xms2g -Xmx2g -XX:+UseG1GC -XX:MaxGCPauseMillis=20 -XX:G1HeapRegionSize=1m -XX:G1MixedGCCountTarget=10 -Xloggc:/data/server/${app}/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime - -Xms2g -Xmx2g -XX:+UseG1GC -XX:MaxGCPauseMillis=100 // 调大停顿时间 -XX:G1HeapRegionSize=2M # 调大region的大小来减少大对象占用的region数 -XX:MaxTenuringThreshold=5 -XX:InitiatingHeapOccupancyPercent=40 #手动设置IHOP。 -XX:ConcGCThreads=4 //当前核心数的一半 -XX:+UseStringDeduplication -XX:AutoBoxCacheMax=20000 -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m -XX:+PrintPromotionFailure -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./ -Xlog:gc*:./gc-myapp.log -XX:+DisableExplicitGC //禁止显示手动GC,如果系统使用了堆外内存就不要禁用 -XX:G1MixedGCCountTarget=10 // 把old区的回收分布到更多次GC当中 - https://zhuanlan.zhihu.com/p/444564414 ## 启动参数 ```shell java -server -XX:+UseContainerSupport -XX:InitialRAMPercentage=70.0 -XX:MaxRAMPercentage=70.0 -XX:+UseG1GC -XX:MaxGCPauseMillis=20 -XX:G1HeapRegionSize=1M -XX:InitiatingHeapOccupancyPercent=45 -XX:+UseStringDeduplication -Ddruid.mysql.usePingMethod=false -javaagent:/app/skywalking-agent/skywalking-agent.jar -Dskywalking.agent.service_name=qifu-saas-wms-prod -Dskywalking.collector.backend_service=oap.qifu.svc:11800 -jar -javaagent:/app/jmx_prometheus_javaagent-0.20.0.jar=8088:/app/jmx_exporter.yaml /app/qifu-saas-wms-service-1.0.13.jar --spring.config.location=/app/config/bootstrap.yml ``` ### 参数说明 - https://www.cnblogs.com/yu007/p/17900844.html - 容器环境替代 `-Xmx -Xms`: - ```shell -XX:+UseContainerSupport -XX:InitialRAMPercentage=70.0 -XX:MaxRAMPercentage=70.0 ``` #### 详细说明 ```shell #- 使用G1垃圾收集器,在低延迟和高吞吐间寻找平衡,可以调整最大停止时间,设置新生代大小来提高吞吐量 -XX:+UseG1GC #- 堆内存,示例设置最大最小值为4g,对于G1,一般建议2g以上。注意设定Xms=Xmx,防止发生扩容、缩容 -Xms4g -Xmx4g #- 配置元空间初始256m、最大256m。不配置的话,元空间会不受限地占用物理机内存 -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m #- 设置最大暂停时间,默认200ms。G1收集回收器将堆进行分区,划分为一个个的区域,每次收集的时候,只收集其中几个区域,以此来控制垃圾回收产生一次停顿时间。 #- 暂停时间只是一个目标,并不能总是得到满足,G1会逐步调整到最佳状态。但若暂停时间设置的太短,就会导致出现G1跟不上垃圾产生的速度,最终退化成Full GC。 #- 在极端环境(如要求业务接口需要快速返回结果)下可尝试设置20ms以下,此时建议:精简下应用服务(减少实例的产生)、升级高版本jdk(17)、做好压测、长期关注。 -XX:MaxGCPauseMillis=100 #- 指定Region大小,必须是2次幂,最大是32m,具体取值有1MB、2MB、4MB、8MB、16MB、32MB。注意当对象占用内存超过Region的一半时将被视为大对象,被分配到Humongous区域 #- 可以预估应用服务的对象大小,确定Region大小。如果没有大对象的场景,则可尝试配置2m、1m,使单个Region能被快速回收 #- 该配置非必填,不声明时,Region大小等于堆大小除以2048 -XX:G1HeapRegionSize=2m #- 针对混合回收回收的参数:混合回收不仅针对老年代,还有新生代和大对象 #- 老年代Region触发混合GC的占比,默认值是45,也就是说老年代占据了堆内存45%的Region的时,会触发混合GC。该值一般不需要调整,这样可以让JVM内存占用维持在50%左右 -XX:InitiatingHeapOccupancyPercent=45 #- 混合回收阶段会执行8次(默认值),一次只回收掉部分Region,然后系统继续运行一小段时间,之后再继续混合回收,重复8轮。混合回收通过间断操作,可以把每次的回收时间控制在指定的停顿时间之内,最终也达到了垃圾清理的效果。 -XX:G1MixedGCCountTarget=8 #- 混合回收整理出来的空闲空间占heap的5%时(默认值),终止本次回收 -XX:G1HeapWastePercent=5 #- 如果一个Region中的存活对象大于Region大小的85%的话(默认值),就不去回收这个Region -XX:G1MixedGCLiveThresholdPercent=85 #- 设置新生代大小,默认5%,默认最大60%。在运行过程中,JVM会不停的给年轻代增加更多的Region,但是最多新生代的占比不会超过G1MaxNewSizePercent值 #- 年轻代中的Eden和Survivor对应的region也跟之前 一样,默认8:1:1,例如年轻代现在有1000个region,eden区对应800个,s0对应100个,s1对应 100个 #- 理论上,不同的应用、不同的CPU硬件资源,都会有不同的最优值,但区别不会太大。通过实践总结,调整该值的收益远不如调整其他参数(如上面提到的几个) -XX:G1NewSizePercent=5 -XX:G1MaxNewSizePercent=60 #- gc日志打印到执行日志文件 -Xloggc:/data/server/${app}/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime #- 可以生成更详细的Survivor空间占用日志,开发环境调试用 -XX:+PrintAdaptiveSizePolicy #- 在控制台输出GC情况,一般是本地调试用。(上线后是后台运行,不关注控制台日志) -verbose:gc #- 开启远程debug,开发环境、测试环境可考虑配置 -Xdebug -Xrunjdwp:transport=dt_socket,address=9555,server=y,suspend=n #- spring相关,指定加载配置文件 --spring.config.location=classpath:/,classpath:/config/,file:./,file:./config/,file:/home/mall-job/conf/ ``` #### 其余参数 ```shell #- 新时代E区和S区比例,默认8:1:1。一般来说,不需要调整 -XX:SurvivorRatio=8 #- 异常场景相关参数 -XX:ErrorFile=文件 #- 设置错误日志路径 -XX:ErrorFile=./hs_err_pid%p.log %p为当前进程号 -XX:OnError=命令 #- 错误发生时执行命令 -XX:OnError="gcore %p;dbx - %p" -XX:OnOutOfMemoryError=命令 #- 内存溢出时执行命令 -XX:MaxDirectMemorySize=size #- 设置直接内存最大值 -XX:MaxDirectMemorySize=100m 默认为0 当直接内存达到设置的最大值会FullGC -XX:ObjectAlignmentInBytes=alignment #- 设置java对象的内存对齐,默认是8字节 -XX:ThreadStackSize #- 设置线程栈大小 -XX:ThreadStackSize=1m = -Xss -XX:-UseBiasedLocking #- 禁用偏向锁 默认开启,不禁用 如果使用的是大量的没有竞争的同步,使用偏向锁会提升性能 -XX:-UseCompressedOops #- 禁用压缩指针堆内存小于32G时默认开启 开启后,对象引用是32位而不是64位,可以提升性能。只有64位的jvm才生效 -XX:+DoEscapeAnalysis #- 开启逃逸分析 默认开启 -XX:+Inline #- 开启方法内联 默认开启 -XX:InlineSmallCode=大小 #- 设置应内联的已编译方法的最大代码大小,只有小于此数值的才会内联 默认1000字节 -XX:MaxInlineSize=大小 #- 设置要内联方法的最大字节码大小 默认35字节 -XX:+OptimizeStringConcat #- 开启字符串连接优化 默认开启 -XX:+PrintInlining #- 打印方法内联 默认关闭,需和-XX:+UnlockDiagnosticVMOptions 一起使用 -XX:-TieredCompilation #- 关闭分层编译 默认开启 -XX:+HeapDumpOnOutOfMemoryError #- OOM时堆内存dump到当前目录 -XX:HeapDumpPath=路径 #- 设置堆内存dump的路径 -XX:HeapDumpPath=/var/java_pid%p.hprof -XX:+UnlockDiagnosticVMOptions #- 开启jvm诊断功能选项 #- 垃圾回收相关参数 -XX:+AggressiveHeap #开启堆最优化设置 默认关闭 -XX:+CMSClassUnloadingEnabled #当使用CMS垃圾收集器时,允许类卸载 默认开启 -XX:CMSExpAvgFactor=percent #指定垃圾收集消耗的时间百分比 默认这个数是25%,就是25 -XX:CMSInitiatingOccupancyFraction=percent #设置CMS回收开始的老年代百分比 默认-1,任何的负值表示会使用-XX:CMSTriggerRatio选项来定义这个百分比数 -XX:+CMSScavengeBeforeRemark #- 在CMS重新标记之前执行ygc操作 默认关闭 在remark时间过长时可以开启;开启减少remark的STW时间 -XX:CMSTriggerRatio=percent #- 设置CMS开始的百分比 默认80,((100 - MinHeapFreeRatio) + (double)( CMSTriggerRatio * MinHeapFreeRatio) / 100.0) / 100.0=92% -XX:+UseCMSCompactAtFullCollection #- 在FULL GC的时候,对年老代的压缩 -XX:CMSFullGCsBeforeCompaction=0 #- 上面配置开启的情况下,这里设置多少次Full GC后,对年老代进行压缩, -XX:MinHeapFreeRatio=percent #- GC之后堆内存最小剩余百分比,如果小于此值,则自动扩容 默认40% -XX:MaxHeapFreeRatio=percent #- GC之后堆内存最大剩余百分比,如果小于此值,则自动缩容 默认70% -XX:ParallelGCThreads=threads #- 设置Parallel GC的线程数 默认根据cpu个数,<=8,则使用8个,>8个3+5N/81台服务器只有1个jvm时使用默认值较好,如果有n个jvm,cpu个数 / n比较合适 -XX:ConcGCThreads=个数 #- 并发GC的线程数 默认值取决于cpu个数 ConcGCThreads = (ParallelGCThreads + 3)/4 -XX:+DisableExplicitGC #- 使System.gc()显式gc失效 默认不开启, -XX:G1HeapRegionSize=size #- 当使用G1收集器时,设置java堆被分割的region大小 1M~32M默认根据堆内存最优化设置 -XX:+G1PrintHeapRegions #- 打印G1收集器收集的区域 默认关闭 -XX:G1ReservePercent=percent #- 设置堆内存保留大小,以防晋升失败 0~50 默认10% -XX:InitialHeapSize=size #- 堆初始大小 =-Xms -XX:MaxHeapSize=size #- 堆最大大小 =-Xmx -XX:InitialSurvivorRatio=ratio #- 设置伊甸园区和幸存区初始比例 默认为8:1 -XX:SurvivorRatio=ratio #- 设置伊甸园区和幸存区比例 默认为8:1 8:1:1 -XX:TargetSurvivorRatio #- YGC之后,幸存区期望百分比 默认 50% -XX:InitiatingHeapOccupancyPercent=percent #- 堆占用达到多少开始并发垃圾回收 只有并发垃圾回收器生效 -XX:MaxGCPauseMillis=time #- GC最大暂停时间 ms 默认没有最大暂停时间 -XX:MetaspaceSize=size #- 元空间多次扩容后超过此值就会full gc 默认大约20M 元空间使用本地内存。 尽量设置足够,避免因为这个区域内存不够引发Full GC -XX:MaxMetaspaceSize=size #- 设置元空间最大大小 默认不限制 建议和MetaspaceSize一样大,一般256M -XX:NewSize=size #- 设置年轻代初始大小 建议年轻代占堆大小的1/4 ~ 1/2 -XX:MaxNewSize=size #- 设置年轻代最大大小 默认根据最大效能分配 -XX:MaxTenuringThreshold=threshold #- 最大晋升年龄,从年轻代到老年代 默认:15 - 并行回收器 6 - CMS -XX:NewRatio=ratio #- 设置老年代和新生代比例 默认2 老年代 : (伊甸园 + 2个幸存区) -XX:+PrintGC #- 打印GC信息 默认关闭 -XX:+PrintGCDetails #- 打印GC详细信息 默认关闭 -XX:+PrintTenuringDistribution #- 打印晋升分配 -XX:+ScavengeBeforeFullGC #- Full gc之前先ygc 默认开启 -XX:+UseTLAB #- 年轻代使用线程局部缓存 默认开启 效率高 -XX:TLABSize=size #- 设置初始化thread-local allocation buffer (TLAB)大小 -XX:+UseAdaptiveSizePolicy #- JDK 1.8 默认使用 UseParallelGC 垃圾回收器,该垃圾回收器默认启动了 AdaptiveSizePolicy -XX:+UseParallelGC #- 年轻代使用并行回收器 -XX:+UseParallelOldGC #- 老年代使用并行回收器 -XX:+UseParNewGC #- 为配置CMS,年轻代使用ParNew回收器,CMS会默认开启 -XX:+UseConcMarkSweepGC #- 使用CMS回收器 -XX:+UseG1GC #- 使用G1回收器 -XX:+UseGCOverheadLimit #- 限制GC的运行时间,通过统计GC时间来预测是否要OOM了,提前抛出异常,防止OOM发生 并行/并发回收器在GC回收时间过长时会抛出OutOfMemroyError。过长的定义是,超过98%的时间用来做GC并且回收了不到2%的堆内存。用来避免内存过小造成应用不能正常工作 -XX:+UseStringDeduplication #- 开启字符串去重 G1回收器生效 -XX:AutoBoxCacheMax=20000 #- 加大Integer Cache -XX:+PrintPromotionFailure #- 知道是多大的新生代对象晋升到老生代失败从而引发Full GC时的。 ```