G1GC
-XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:-OmitStackTraceInFastThrow -Xlog:gc*=debug:file=${LOG_PATH}/gc%t.log:utctime,level,tags:filecount=50,filesize=100M -Xlog:jit+compilation=info:file=${LOG_PATH}/jit_compile%t.log:utctime,level,tags:filecount=10,filesize=10M -Xlog:safepoint=debug:file=${LOG_PATH}/safepoint%t.log:utctime,level,tags:filecount=10,filesize=10M -Dfile.encoding=UTF-8 -Djava.security.egd=file:/dev/./urandom -Dnetworkaddress.cache.ttl=10 -XX:MaxRAMPercentage=45 -XX:InitialRAMPercentage=45 -XX:+AlwaysPreTouch -Xss512k -XX:MaxDirectMemorySize=1024m -XX:MaxMetaspaceSize=384m -XX:ReservedCodeCacheSize=256m -XX:+DisableExplicitGC -XX:MaxGCPauseMillis=50 -XX:-UseBiasedLocking -XX:GuaranteedSafepointInterval=0 -XX:+UseCountedLoopSafepoints -XX:+SafepointTimeout -XX:SafepointTimeoutDelay=1000 -XX:StartFlightRecording=disk=true,maxsize=4096m,maxage=3d -XX:FlightRecorderOptions=maxchunksize=128m --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.base/java.math=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED --add-opens java.base/java.nio=ALL-UNNAMED --add-opens java.base/java.security=ALL-UNNAMED --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.base/java.time=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/jdk.internal.access=ALL-UNNAMED --add-opens java.base/jdk.internal.misc=ALL-UNNAMED
ZGC
-XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:-OmitStackTraceInFastThrow -Xlog:gc*=debug:file=${LOG_PATH}/gc%t.log:utctime,level,tags:filecount=50,filesize=100M -Xlog:jit+compilation=info:file=${LOG_PATH}/jit_compile%t.log:utctime,level,tags:filecount=10,filesize=10M -Xlog:safepoint=debug:file=${LOG_PATH}/safepoint%t.log:utctime,level,tags:filecount=10,filesize=10M -Dfile.encoding=UTF-8 -Djava.security.egd=file:/dev/./urandom -Dnetworkaddress.cache.ttl=10 -XX:MaxRAMPercentage=45 -XX:InitialRAMPercentage=45 -XX:+AlwaysPreTouch -Xss512k -XX:MaxDirectMemorySize=1024m -XX:MaxMetaspaceSize=384m -XX:ReservedCodeCacheSize=256m -XX:+DisableExplicitGC -XX:+UseZGC -XX:-UseBiasedLocking -XX:GuaranteedSafepointInterval=0 -XX:+UseCountedLoopSafepoints -XX:+SafepointTimeout -XX:SafepointTimeoutDelay=1000 -XX:StartFlightRecording=disk=true,maxsize=4096m,maxage=3d -XX:FlightRecorderOptions=maxchunksize=128m --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.base/java.math=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED --add-opens java.base/java.nio=ALL-UNNAMED --add-opens java.base/java.security=ALL-UNNAMED --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.base/java.time=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/jdk.internal.access=ALL-UNNAMED --add-opens java.base/jdk.internal.misc=ALL-UNNAMED
-XX:MaxRAMPercentage=45 -XX:InitialRAMPercentage=45 -Xss512k -XX:MaxDirectMemorySize=1024m -XX:MaxMetaspaceSize=384m -XX:ReservedCodeCacheSize=256m
里面的参数-XX:StartFlightRecording=disk=true,maxsize=4096m,maxage=3d
其中的 4096m 以及 3d-XX:MaxGCPauseMillis=50
:这个只有使用 G1GC 的需要
需要异步输出日志,防止卡死 JVM:
-Xlog:async
GC日志:
-Xlog:gc*=debug:file=${LOG_PATH}/gc%t.log:utctime,level,tags:filecount=50,filesize=100M
JIT 编译日志:
-Xlog:jit+compilation=info:file=${LOG_PATH}/jit_compile%t.log:utctime,level,tags:filecount=10,filesize=10M
Safepoint 日志:
-Xlog:safepoint=debug:file=${LOG_PATH}/safepoint%t.log:utctime,level,tags:filecount=10,filesize=10M
关闭堆栈省略:这个只会省略 JDK 内部的异常,比如 NullPointerException 这种的:
-XX:-OmitStackTraceInFastThrow
,我们应用已经对于大量报错的时候输出大量堆栈导致性能压力的优化,-Dfile.encoding=UTF-8
:指定编码为 UTF-8,其实 Java 18 之后默认编码就是 UTF-8 了,这样避免不同操作系统编译带来的差异(Windows 默认是 GB2312,Linux 默认是 UTF-8),参考:https://openjdk.java.net/jeps/400-Djava.security.egd=file:/dev/./urandom
:更换 random 为 urandom 避免高并发加密证书通信的时候的生成随机数带来的阻塞(例如高并发 https 请求,高并发 mysql 连接通信),参考:https://zhuanlan.zhihu.com/p/259874076-Dnetworkaddress.cache.ttl=10
:将 DNS 缓存降低为 10s 过期,咱们 k8s 内部有很多通过域名解析的资源(通过 k8s 的 coreDNS),解析的 ip 可能会过期,漂移成新的 ip,默认的 30s 有点久,改成 10s,但是这会增加 coreDNS 的压力。堆内存控制:
-XX:MaxRAMPercentage=45 -XX:InitialRAMPercentage=45 -XX:MinHeapFreeRatio=0 -XX:MaxHeapFreeRatio=100
(使用系统可用内存百分比配置堆,堆内存一般占用系统内存的少一半,其他的给堆外内存使用,比如 GC,元空间,Tracing 等等,同时设置 MinHeapFreeRatio 为最小值,MaxHeapFreeRatio 为最大值是为了避免 GC 的时候动态伸缩堆大小)线程栈大小控制:
-Xss512k
直接内存(各种 Direct Buffer)大小控制:
-XX:MaxDirectMemorySize=1024m
元空间控制:
-XX:MaxMetaspaceSize=384m
JIT 即时编译后(C1 C2 编译器优化)的代码占用内存:
-XX:ReservedCodeCacheSize=256m
内存初始化(针对堆内存,申请并 commit 了InitialRAMPercentage大小后立刻填充 0 让系统真的把内存分配给 JVM):
-XX:+AlwaysPreTouch
通用参数:
-XX:+DisableExplicitGC
:关闭显示 GC(System.gc()触发的 FullGC),防止 netty 这种误检测内存泄漏显示调用
G1GC 参数:
Java 9 之后默认 GC 就是 G1GC,所以不用显示指定使用 G1GC
在 Java 14 之后 G1GC 有巨大突破,目前 Java 17 中已经不需要调非常复杂的参数了,可以只调整目标最大 STW(Stop-the-world) 时间来均衡 CPU 占用,内存占用与延迟。
-XX:MaxGCPauseMillis=50
:目标最大 STW(Stop-the-world) 时间,这个越小,GC 占用 CPU 资源,占用内存资源就越多,微服务吞吐量就越小,但是延迟低。这个需要做成可配置的
ZGC 参数:
ZGC 不用调优,是自适应的
-XX:+UseZGC
:使用 ZGC
安全点控制
关于安全点,可以查看这篇文章:https://zhuanlan.zhihu.com/p/161710652
-XX:-UseBiasedLocking
:禁用偏向锁,偏向锁其实未来会被完全移除(参考:),目前咱们都是高并发的环境,偏向锁基本没啥用并且还有负面影响-XX:GuaranteedSafepointInterval=0
:禁用定时安全点任务,没必要,咱们不是那种热点代码经常改变,资源珍贵的场景,并且如果是 ZGC 本身就会定时进入安全点进行 GC 检查,更没必要了-XX:+UseCountedLoopSafepoints
:防止大有界循环带来的迟迟不进入安全点导致 GC STW 时间过长-XX:+SafepointTimeout -XX:SafepointTimeoutDelay=1000
:防止其他情况下导致进入安全点时间过长导致 STW 时间过长,这里配置的是 1s。但是没有指定 AbortVMOnSafepointTimeout 为 true,所以对你的 jmap 以及 jstack 命令没有影响
JFR 配置
JFR 使用请参考:https://zhuanlan.zhihu.com/p/161710652
-XX:StartFlightRecording=disk=true,maxsize=4096m,maxage=3d -XX:FlightRecorderOptions=maxchunksize=128m
模块化限制
--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.base/java.math=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED --add-opens java.base/java.nio=ALL-UNNAMED --add-opens java.base/java.security=ALL-UNNAMED --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.base/java.time=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/jdk.internal.access=ALL-UNNAMED --add-opens java.base/jdk.internal.misc=ALL-UNNAMED
Java 16 将 --illegal-access
的默认值从 permit 改成了 deny (JEP: https://openjdk.java.net/jeps/396),Java 17 直接移除了这个选项 (JEP: https://openjdk.java.net/jeps/403),所以现在要打破模块化封装,必须通过这个命令具体打破某些模块向某些模块的暴露。这里包含了一些常用的可能会被反射访问的 java.base 下的 package,向所有未命名模块暴露(我们自己的项目一般不会指定模块名,如果你指定了就换成具体你的模块名)