参考文献

JVM参数说明

1
2
3
java [options] classname [args]

java [options] -jar filename [args]
  • [options] 部分称为JVM 选项,对应 IDE 中的 VM options, 可用 jps -v 查看.

  • [args] 部分是指传给main函数的参数, 对应 IDE 中的 Program arguments, 可用 jps -m 查看.

  • Java 和 JDK 内置的工具,指定参数时都是一个 -,不管是长参数还是短参数.有时候,JVM 启动参数和 Java 程序启动参数,并没必要严格区分,大致知道都是一个概念即可.

  • JVM 的启动参数, 从形式上可以简单分为:

    • -开头为标准参数,所有的 JVM 都要实现这些参数,并且向后兼容.
    • -X开头为非标准参数, 基本都是传给 JVM 的,默认 JVM 实现这些参数的功能,但是并不保证所有 JVM 实现都满足,且不保证向后兼容.
    • -XX:开头为非稳定参数, 专门用于控制 JVM 的行为,跟具体的 JVM 实现有关,随时可能会在下个版本取消.
    • -XX:+-Flags 形式, +- 是对布尔值进行开关.
    • -XX:key=value 形式, 指定某个选项的值.

设置系统属性

  • 系统属性,有时候也叫环境变量,例如直接给 JVM 传递指定的系统属性参数,需要使用 -Dkey=value 这种形式,此时如果系统的环境变量里不管有没有指定这个参数,都会以这里的为准.

  • 命令行参数,直接通过命令后面添加的参数,比如运行 Hello 类,同时传递 2 个参数 kimm、king:java Hello kimm king,然后在Hello类的 main 方法的参数里可以拿到一个字符串的参数数组,有两个字符串,kimm 和 king.

  • 查看默认的所有系统属性

    1
    java -XshowSettings:properties -version
  • 查看VM设置

    1
    java -XshowSettings:vm -version
  • 查看当前 JDK/JRE 的默认显示语言设置:

    1
    java -XshowSettings:locale -version

Agent相关的选项

  • 设置 agent 的语法如下:
    • -agentlib:libname[=options] 启用native方式的agent, 参考 LD_LIBRARY_PATH 路径.
    • -agentpath:pathname[=options] 启用native方式的agent.
    • -javaagent:jarpath[=options] 启用外部的agent库, 比如 pinpoint.jar 等等.
    • -Xnoagent 则是禁用所有 agent.

JVM运行模式

  • JVM 有两种运行模式:

    • -server:设置 jvm 使 server 模式,特点是启动速度比较慢,但运行时性能和内存管理效率很高,适用于生产环境.在具有 64 位能力的 jdk 环境下将默认启用该模式,而忽略 -client 参数.
    • -client :JDK1.7 之前在 32 位的 x86 机器上的默认值是 -client 选项.设置 jvm 使用 client 模式,特点是启动速度比较快,但运行时性能和内存管理效率不高,通常用于客户端应用程序或者PC应用开发和调试.
  • 此外, JVM 加载字节码后,可以解释执行,也可以编译成本地代码再执行,所以可以配置 JVM 对字节码的处理模式:

    • -Xint:在解释模式(interpreted mode)下,-Xint 标记会强制 JVM 解释执行所有的字节码,这当然会降低运行速度,通常低 10 倍或更多.
    • -Xcomp:-Xcomp 参数与 -Xint 正好相反,JVM 在第一次使用时会把所有的字节码编译成本地代码,从而带来最大程度的优化.
    • -Xmixed:-Xmixed 是混合模式,将解释模式和变异模式进行混合使用,有 JVM 自己决定,这是 JVM 的默认模式,也是推荐模式. 我们使用 java -version 可以看到 mixed mode 等信息.

设置堆内存

  • JVM 的内存设置是最重要的参数设置,也是 GC 分析和调优的重点.

    JVM 总内存=堆+栈+非堆+堆外内存.

  • 相关的参数:

    • -Xmx, 指定最大堆内存. 如 -Xmx4g. 这只是限制了 Heap 部分的最大值为 4g.这个内存不包括栈内存,也不包括堆外使用的内存.推荐配置系统或容器里可用内存的 70-80% 最好

    • -Xms, 指定堆内存空间的初始大小. 如 -Xms4g. 而且指定的内存大小,并不是操作系统实际分配的初始值,而是 GC 先规划好,用到才分配. 专用服务器上需要保持 -Xms-Xmx一致,否则应用刚启动可能就有好几个 FullGC.当两者配置不一致时,堆内存扩容可能会导致性能抖动.

    • -Xmn, 等价于 -XX:NewSize,使用 G1 垃圾收集器 不应该 设置该选项,在其他的某些业务场景下可以设置.官方建议设置为 -Xmx1/2 ~ 1/4.

    • -XX:MaxPermSize=size, 这是 JDK1.7 之前使用的.Java8 默认允许的 Meta 空间无限大,此参数无效.

    • -XX:MaxMetaspaceSize=size, Java8 默认不限制 Meta 空间, 一般不允许设置该选项.

    • XX:MaxDirectMemorySize=size,系统可以使用的最大堆外内存,这个参数跟-Dsun.nio.MaxDirectMemorySize效果相同.

    • -Xss, 设置每个线程栈的字节数. 例如 -Xss1m 指定线程栈为 1MB,与-XX:ThreadStackSize=1m等价

  • 堆外内存,也就是说不在堆上的内存,可以通过jconsole,jvisualvm等工具查看.

指定垃圾收集器相关参数

  • -XX:+UseG1GC:使用 G1 垃圾回收器
  • -XX:+UseConcMarkSweepGC:使用 CMS 垃圾回收器
  • -XX:+UseSerialGC:使用串行垃圾回收器
  • -XX:+UseParallelGC:使用并行垃圾回收器

GC日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-XX:+UseGCLogFileRotation 
-XX:NumberOfGCLogFiles=< number of log files >
-XX:GCLogFileSize=< file size >[ unit ]
-Xloggc:/path/to/gc.log

example:
// 如果我们要分配最多 100 个GC日志文件,每个最大大小为 50 MB,并且希望将它们存储在'/home/user/log/'位置
-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=10
-XX:GCLogFileSize=50M
-Xloggc:/home/user/log/gc.log

// Java 11
// java -Xlog:gc -Xlog:gc*=debug:file=gc.log:time,uptimemillis:filecount=10,filesize=1024m
  • -XX:+UseGCLogFileRotation : 表示指定日志文件滚动策略
  • -XX:NumberOfGCLogFiles: 表示单个应用程序生命周期可以写入的最大日志文件数
  • -XX:GCLogFileSize: 表示指定文件的最大大小
  • -Xloggc: 表示GC文件所在位置

特殊情况执行脚本的参数

  • -XX:+HeapDumpOnOutOfMemoryError 选项, 当 OutOfMemoryError 产生,即内存溢出(堆内存或持久代)时,自动 Dump 堆内存. 因为在运行时并没有什么开销, 所以在生产机器上是可以使用的. 示例用法: java -XX:+HeapDumpOnOutOfMemoryError -Xmx256m ConsumeHeap
  • -XX:HeapDumpPath 选项, 与HeapDumpOnOutOfMemoryError搭配使用, 指定内存溢出时 Dump 文件的目录. 如果没有指定则默认为启动 Java 程序的工作目录. 示例用法: java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/ ConsumeHeap 自动 Dump 的 hprof 文件会存储到 /usr/local/ 目录下.
  • -XX:OnError 选项, 发生致命错误时(fatal error)执行的脚本. 例如, 写一个脚本来记录出错时间, 执行一些命令, 或者 curl 一下某个在线报警的url. 示例用法: java -XX:OnError="gdb - %p" MyApp 可以发现有一个 %p 的格式化字符串,表示进程 PID.
  • -XX:OnOutOfMemoryError 选项, 抛出OutOfMemoryError错误时执行的脚本.
  • -XX:ErrorFile=filename 选项, 致命错误的日志文件名,绝对路径或者相对路径.

打印JVM参数

  • 打印JVM默认配置

  • 使用 -XX:+PrintCommandLineFlags 可以查询 Java 启动时定义好的变量,用它可以看到使用的 GC。

1
2
3
4
5
# java -XX:+PrintCommandLineFlags -version
-XX:ConcGCThreads=2 -XX:G1ConcRefinementThreads=8 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=268435456 -XX:MarkStackSize=4194304 -XX:MaxHeapSize=4294967296 -XX:MinHeapSize=6815736 -XX:+PrintCommandLineFlags -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC
openjdk version "17.0.4" 2022-07-19 LTS
OpenJDK Runtime Environment Zulu17.36+13-CA (build 17.0.4+8-LTS)
OpenJDK 64-Bit Server VM Zulu17.36+13-CA (build 17.0.4+8-LTS, mixed mode, sharing)
  • 显示所有可设置的参数以及它们的值
1
2
java -XX:+PrintFlagsFinal -XX:+UseG1GC  2>&1
java -XX:+PrintFlagsInitial -XX:+UseG1GC 2>&1
  • 打印VM启动时传入的参数

    1
    2
    3
    # jinfo -flags PID
    VM Flags:
    -XX:CICompilerCount=3 -XX:ConcGCThreads=1 -XX:G1ConcRefinementThreads=4 -XX:G1HeapRegionSize=2097152 -XX:GCDrainStackTargetSize=64 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log -XX:InitialHeapSize=4294967296 -XX:MarkStackSize=4194304 -XX:MaxHeapSize=4294967296 -XX:MaxNewSize=2575302656 -XX:MinHeapDeltaBytes=2097152 -XX:NonNMethodCodeHeapSize=5830732 -XX:NonProfiledCodeHeapSize=122913754 -XX:ProfiledCodeHeapSize=122913754 -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseG1GC

参数建议

各回收器优化参数

基本参数
  • -Xmx(一般为容器内存的50%)
  • -Xms(与Xmx一致)
  • -XX:MetaspaceSize(通常256M~512M)
  • -XX:ParallelGCThreads=容器核数
  • -XX:CICompilerCount=容器核数(必须大于等于2)
ParallelGC
  • 除以上参数外,一般不需要额外调优(JDK8默认回收器)
CMS
  • -XX:+UseConcMarkSweepGC
  • -Xmn (一般为堆内存的三分之一),尤其是配置了ParallelGCThreads后必须配置此参数
  • -XX:ConcGCThreads=n(默认为ParallelGCThreads/4,可视情况调整至ParallelGCThreads/2)
  • -XX:+UseCMSInitiatingOccupancyOnly
  • -XX:CMSInitiatingOccupancyFraction=70(推荐值)
G1
  • -XX:+UseG1GC

  • -XX:ConcGCThreads=n(默认为ParallelGCThreads/4,可视情况调整至ParallelGCThreads/2)

  • -XX:G1HeapRegionSize=8m(若堆内存在8G以内且有较多大对象推荐设置此值)

  • 注意不要设置-Xmn-XX:NewRatio

    • 不建议同时设置 -Xmn-XX:NewRatio 参数.这是因为 G1 回收器的内存分配方式与传统的分代式回收器不同.
    • G1 回收器将整个堆空间划分为多个固定大小的区域(Region),每个区域可以是 Eden 区、Survivor 区或 Old 区.这种分区的方式使得 G1 回收器能够更精确地控制垃圾收集的区域.
    • 因此,设置 -Xmn 参数来定义新生代的大小是没有意义的,而且可能会导致性能问题.同样,设置 -XX:NewRatio 参数来调整新生代和老年代的比例也不适用于 G1 回收器.
    • G1 回收器会根据实际的工作负载和内存需求自动调整各个区域的大小和比例.因此,不需要手动调整和限制新生代的大小.
    • 由于 G1 回收器具有自适应的内存管理策略,因此在绝大多数情况下,不需要手动调整和设置新生代参数.只需要使用 -XX:+UseG1GC 启用 G1 回收器即可.
其他调优参数
  • -XX:+ParallelRefProcEnabled 如果GC时Reference处理时间较长,例如大量使用WeakReference对象,可以通过此参数开启并行处理

开启GC日志

  • -XX:+PrintGCDetails
  • -XX:+PrintGCDateStamps
  • -Xloggc:/export/Logs/gc.log

如何判断GC是否正常

  • GC是否频繁:YoungGC频率一般几十秒钟一次,FullGC一般每天几次,注意G1回收器不应该出现FullGC.
  • GC耗时:耗时主要取决于堆内存大小及垃圾对象数量.YoungGC时间通常应在几十毫秒,FullGC通常在几百毫秒.
  • 每次GC内存是否下降:应用刚启动时,每次YoungGC内存应该回收到较低水位,随着时间推移老年代逐步增多,内存水位会逐步上涨,直到FullGC/MixedGC(G1),内存会再次回到较低水位,否则可能存在内存泄漏.
  • 如果使用ParallelGC,堆内存耗尽才会触发FullGC,所以不用配置堆内存使用率告警,但需关注GC频率.

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 设置堆内存
-Xmx4g -Xms4g
# 指定 GC 算法
-XX:+UseG1GC
-XX:MaxGCPauseMillis=50
# 指定 GC 并行线程数
-XX:ParallelGCThreads=4
# 打印 GC 日志
-Xlog:gc*,gc+ref=debug,gc+heap=debug,gc+age=trace:file=gc-%p-%t.log:tags,uptime,time,level:filecount=10,filesize=50m:/var/log/gc/gc.log
# 指定 Meta 区的最大值
-XX:MaxMetaspaceSize=2g
# 设置单个线程栈的大小
-Xss1m
# 指定堆内存溢出时自动进行 Dump
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/usr/local/
1
2
3
4
5
6
7
8
9
# 指定默认的连接超时时间
-Dsun.net.client.defaultConnectTimeout=2000
-Dsun.net.client.defaultReadTimeout=2000
# 指定时区
-Duser.timezone=GMT+08
# 设置默认的文件编码为 UTF-8
-Dfile.encoding=UTF-8
# 指定随机数熵源(Entropy Source)
-Djava.security.egd=file:/dev/./urandom