# 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时的。

```