INDEX
§1 JVM 调优目的与指标
JVM 调优的指标有 3 个
- 吞吐量:用户代码运行时间/总运行时间
- 暂停时间:工作线程被暂停的时间
- 内存占用:占用的堆的大小
其实上面指标只说了两个事:让 JVM 多干活 & 让 JVM 少占资源
但这两个事实际上是互斥的。
吞吐量降低其实就是因为暂停时间变长,暂停时间变长是为了 GC。GC 是为了收拾 JVM 干活过程中产生的垃圾,那么当然 JVM 可用的空间越小越需要频繁的收拾。
通常,我们认为 JVM 调优的目的是使
- 吞吐量:
- 单次 Minor GC < 200ms
- 单次 Full GC < 5s
§2 常见涉及参数
内存大小五件套
-Xmx
:最大堆内存,
-Xms
:最小堆内存(初始值),需要和最大内存保持一致,否则可能刚启动 JVM 就频繁 Full GC 扩容,极端情况下堆可能不能扩展到最大值
-Xmn
:年轻代大小
-Xms
:栈大小,默认 1M,通常不变。
-XX:MetaspaceSize
:元空间大小,通常不设置
堆内存主要估值方法有两种:
- 用 JVM 可用最大内存估算:堆占 2/3 系统内存,新生代占 1/3 堆
- 用 JVM 实际运行数据估算:主要依赖发生过 Full GC 后老年代的大小为指标
推荐使用第二种,因目前项目部署多实用容器化部署,硬件资源多数不是一个 JVM 独占
-Xms
在线程变量极大的情况下可以调大,反之可以调小(一般 项目中保留 256K 足够使用)。
调小此参数的另一个意义在于可以使 JVM 开辟更多的线程。但是,按传统线程估算方式,最大不过 2N,且线程过多后切换线程上下文的开销也很客观,因此如无特殊需要不没必要特意为了这个原因去调整。
推荐的配置方式:
- 部署服务,进行压测,记录 Full GC 发生后老年代的稳定大小 x
- 老年代大小需要 2-3 倍 x
- 新生代大小需要 1-1.5 倍 x
- 元空间大小事动态向操作系统申请的,建议不设置
- 综上:
-Xmx
==-Xms
== 3-4 x-Xmn
== 1-1.5 x-Xss
保持默认-XX:MetaspaceSize
保持默认
线上问题填坑必备系列
-XX:+HeapDumpOnOutOfMemoryError
:堆 oom 后输出 dump 文件
-XX:HeapDumpPath
:dump 文件的输出路径
内存比例系列
-XX:SurvivorRatio
:eden 相对于当个 survivor 区的大小
其他通用参数
-XX:PreTenuringThreshold
:大对象阈值
GC 手搓
-XX:DisableExplicitGC
:禁用 System.gc()
,生产环境开启(开启就是禁用)
GC 打印系列
-XX:+PrintGC
:打印 GC 基本信息
-XX:+PrintGCDetails
:打印 GC 详细信息
-XX:+PrintHeapAtGC
:GC 发生后打印堆信息
-XX:+PrintGCTimeStamps
:在 GC 上打印时间戳
-XX:+PrintGCApplicationConcurrentTime
:打印应用程序时间
-XX:+PrintGCApplicationStorppedTime
:打印暂停时长
此系列参数一般在生产环境上都禁止,因为会影响性能
但可以在测试环境中开启,发生线上问题准备复现时也可以在沙箱中开启
Flags 系列
-XX:+PrintFlagsInitial
:打印 JVM 参数的初始化值
-XX:+PrintFlagsFinal
:打印 JVM 参数的最终应用值
Parallel 系列
-XX:+UseParallelGC
:启用 Parallel 垃圾回收新生代,一般启用并行老年代会关联开启并行新生代
-XX:+UseParallelOldGC
:启用 Parallel 垃圾回收
-XX:ParallelGCThreads
:并行 GC 的线程数,一般等于核数
-XX:+UseAdaptiveSizePolicy
:是否自动选择各区大小比例,一般不启用
CMS 系列
-XX:+UseConcMarkSweepGC
:是否启用 CMS
-XX:ParallelGCThreads
:初始标记阶段并行 GC 的线程数,默认 8
-XX:CMSInitiatingOccupancyFraction
:使用多少比例后开始 CMS 收集,默认 2/3,需要在实际环境中根据现象调整
- 值过小:会频繁 GC
- 值过大:可能导致 CMS 收集失败,触发兜底的 SerialOld 收集器导致卡顿
-XX:+UseCMSCompactAtFullCollection
:Full GC 时压缩
-XX:CMSFullGCsBeforeCompaction
:几次 Full GC 后启用压缩
- 想使用此参数,
-XX:+UseCMSCompactAtFullCollection
必须开启 - 配置 0 表示 1 次 FullGC 后压缩,与配置 1 一样
-XX:GCTimeRatio
:GC 时间占程序运行时间比例,一般不使用
会导致强制终止 GC,所以不推荐
-XX:MaxGCPauseMillis
:最大 GC 停顿毫秒值,一般不使用
此配置会使 JVM 用各种方法向这个指标考虑,甚至可能减小年轻代,导致神器的问题
-XX:CMSClassUnloadingEnabled
:启用类卸载,默认开启,一般不更改
-XX:CMSInitiatingPermOccupancyFraction
:什么比例时开始进行永久代 GC,jdk1.8之后无效
G1 系列
-XX:+UseG1GC
:是否启用 G1
-XX:G1HeapRegionSize
:单个 region 大小,1-32M
- 应按 1、2、4、8、16、32 逐级增大
- 默认 1/2048 堆
- 官方推荐 G1 内存 > 6G
- G1 内存开辟具有优先级,元空间 > 老年代 > 新生代
- 因此若内存太小可能导致新生代几乎无空间,开辟到新生代时已经不够用了
- region 变大后,GC 间隔会变长,同时 GC 时间、垃圾生存时间都会变长
-XX:G1NewSizePercent
:最小新生代比例,一般不使用
-XX:G1MaxNewSizePercent
:最大新生代比例,默认 60%
-XX:MaxGCPauseMillis
:最大 GC 停顿毫秒值
这个参数几乎可以说和 -XX:G1NewSizePercent
互斥,因为 G1 可以满足配置的最大停顿时间就是通过 region 角色的灵活切换,但 -XX:G1NewSizePercent
直接限制了 region 的角色
这两个参数对比,通常保留使用 -XX:MaxGCPauseMillis
-XX:GCPauseIntervalMillis
:GC 间隔时间
当 -XX:MaxGCPauseMillis
和 -XX:GCPauseIntervalMillis
参数没有配置时,G1 会用 -XX:G1NewSizePercent
& -XX:G1MaxNewSizePercent
参数为依据自动调整
-XX:GCTimeRatioGC
:GC 时间占程序运行时间比例,一般不使用
会导致强制终止 GC,所以不推荐
-XX:ConcGCThreads
:并行 GC 线程数
-XX:InitiatingHeapOccupancyPercent
:默认 45%,目前已经开辟为 region 的内存,使用多少比例后触发开辟新的 region
§3 其他参数
TLAB 系列
-XX:+UseTLAB
:默认开启的,TLAB 可以辅助减小线程并发访问堆中对象
-XX:+PrintTLAB
:打印 TLAB 使用情况
-XX:TLABSize
:设置 TLAB 大小
分代年龄
-XX:MaxTenuringThreshold
:最大分代年龄,默认 15(CMS 中是 6),一般不动