一提到 JVM
参数调优,脑海中第一眼浮现就是面试八股文,但在日常的开发中,有时间一个不起眼的参数总能给你带来意想不到的效果。
正所谓技多不压身,今天让我们一起来揭开 JVM
调优的神秘面纱。
一、参数配置
1. 配置方式
在虚拟机中 -X
和 -D
都可用于指定 JVM
参数,但它们在使用方式和作用范围上有一些区别。
(1) -X 方式
-X
用于指定非标准的 JVM
参数,这些参数通常是供具体的 JVM
实现或特定的虚拟机选项使用的。
例如 -X<parameter>
,其中 <parameter>
是具体的 JVM
参数名称。
(2) -D 方式
-D
用于设置系统属性,提供了一种在应用程序中传递配置信息的机制,可以在应用程序中通过 System.getProperty()
方法获取。
例如 -D<property>=<value>
,其中 <property>
是属性名称,<value>
是属性值。
(3) 基本格式
JVM
配置参数通常由 -XX:
开头,基本格式如下:
- 若用于配置是否启用,由
+ -
分别控制开启与关闭。- 若用于配置大小或数值变量,遵循
<option>=<value>
格式。
# 表示开启 option 选项
-XX:+<option>
# 表示关闭 option 选项
-XX:-<option>
# 表示将 option 值赋为 value
-XX:<option>=<value>
2. 优先级
在 Java
命令行运行 JAR
包时,JVM
参数可以在 -jar
选项之前或之后指定,这两种方式会影响参数的解析和执行顺序。
- 参数在
-jar
之前,则会在JVM
启动前解析和应用。- 参数在
-jar
之后,则会在解析JAR
包时被应用。- 若
JVM
参数与JAR
包的命令行参数存在冲突,JVM
参数的优先级更高。
如下示例中方式一的 -Xmx512m
优先级高于方式二中的 -Xmx1024m
。
# 方式一
java -Xmx512m -jar myapp.jar
# 方式二
java -jar myapp.jar -Xmx1024m
二、常用配置
1. 内存参数
在默认的 JVM
服务启动中,如果没有手动指定堆内存值,则默认的取值方式如下:
- 最小值:通常是物理内存大小的
1/64
,但不超过1GB
,可通过-Xms
参数指定。 - 最大值:通常是物理内存大小的
1/4
,但不超过32GB
,可通过-Xmx
参数指定。
常见的 JVM
内存参数如下:
# 堆内存初始值
-Xms1G
# 堆内存最大值
-Xmx2G
# 每个线程的堆栈大小,默认 1MB,在相同物理内存下,减小这个值能生成更多的线程
# 但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在 3000~5000 左右
-Xss512k
# 设置年轻代大小,增大年轻代后,将会减小年老代大小。
# 此值对系统性能影响较大,Sun 官方推荐配置为整个堆的 3/8。
-Xmn1024m
2. JVM参数
针对于 JVM
的内存堆 (Heap)
以及垃圾回收,常见的配置参数如下:
# CMS垃圾回收器并行线程线,推荐值为CPU核心数。
-XX:ConcGCThreads=4
# 新生代并行收集器的线程数。
-XX:ParallelGCThreads=8
# 设置新生代中 Eden 与 Survivor 的比例,默认为 8,即 8:1:1
-XX:SurvivorRatio=8
# 直接晋升到老年代的对象大小
# 设置该值后将忽略 MaxTenuringThreshold 设置,当对象大小达到设置值后直接晋升老年代
-XX:PretenureSizeThreshold=1m
# 设置垃圾最大年龄,取值 0~15,默认为 15
-XX:MaxTenuringThreshold=10
3. 系统参数
常见的系统参数配置参数如下:
# 防止虚拟机阻塞
-Djava.security.egd=file:/dev/./urandom
4. GC参数
常见的虚拟机垃圾回收(GC
)参数配置参数如下:
开启日志后生成的 GC
信息文件可通过工具进行分析,推荐在线分析网站:https://gceasy.io
。
# 打印 gc 发生的时间戳
-XX:+PrintGCDateStamps
# 打印 gc 发生时的分代信息
-XX:+PrintTenuringDistribution
# 打印 gc 停顿时长
-XX:+PrintGCApplicationStoppedTime
# 打印 gc 间隔的服务运行时长
-XX:+PrintGCApplicationConcurrentTime
# 打印 gc 详情,包括 gc 前/内存等
-XX:+PrintGCDetails
# 指定 gc log 的路径,存放于当前 gclogs 目录下的 gc.log.date 文件
-Xloggc:./gclogs/gc.log.date
三、JDK工具
1. Jinfo
jinfo
是 JDK
中的一个命令行工具,用于实时查看和调整 Java
虚拟机(JVM
)的配置信息。
通过 jinfo
可以获取和修改正在运行的 Java
进程的虚拟机配置参数,对于调优和诊断 Java
应用程序是很有帮助的。
# 查看进程的虚拟机参数信息
jinfo <options> <pid>
(1) flags
显示所有可用的虚拟机参数及其当前值。
jinfo -flags <pid>
(2) flag
显示指定标志的值。
jinfo -flag <name> <pid>
# 查看 pid 为 123 的 ThreadStackSize 参数信息
# 输出: -XX:ThreadStackSize=1024
jinfo -flag ThreadStackSize 123
(3) -flag [+|-]
启用或禁用指定的标志。
# 开启配置
jinfo -flag +<name> <pid>
# 关闭配置
jinfo -flag -<name> <pid>
(4) sysprops
显示 Java
系统属性的值。
jinfo -sysprops <pid>
2. Jstat
jstat
是 JDK
中的一个命令行工具,用于监视 Java
虚拟机 (JVM)
的各种统计信息。
它提供了对堆内存、垃圾回收、类装载、JIT
编译等方面的实时性能数据,帮助开发人员和系统管理员诊断和分析 Java
应用程序的性能问题。
# <pid>: jps process id
# <interval>: time interval, ms
jstat -gc <pid> <interval>
# Simple info
jstat -gcutil <pid> <interval>
- S0C:年轻代中第一个
Survivor
(幸存区)的容量 (KB) 。 - S1C:年轻代中第二个
Survivor
(幸存区)的容量 (KB)。 - S0U:年轻代中第一个
Survivor
(幸存区)目前已使用空间 (KB) 。 - S1U:年轻代中第二个
Survivor
(幸存区)目前已使用空间 (KB) 。
- EC:年轻代中
Eden
(伊甸园)的容量 (KB) 。 - EU:年轻代中
Eden
(伊甸园)目前已使用空间 (KB)。
- OC:
Old
代的容量 (KB) 。 - OU:
Old
代目前已使用空间 (KB) 。
- MC:元数据区的容量 (KB) 。
- MU:元数据区目前已使用空间 (KB) 。
- YGC:从应用程序启动到采样时年轻代中
GC
次数 。 - YGCT:从应用程序启动到采样时年轻代中
GC
所用时间(秒) 。
- FGC:从应用程序启动到采样时
Old
代Full GC
次数 。 - FGCT:从应用程序启动到采样时
Old
代Full GC
所用时间(秒) 。 - GCT:从应用程序启动到采样时
GC
用的总时间(s) 。
- NGCMN:年轻代 (
Young
) 中初始化(最小)的大小 (字节) 。 - NGCMX:年轻代 (
Young
) 的最大容量 (字节) 。 - NGC:年轻代 (
Young
) 中当前的容量 (字节) 。
- OGCMN:
Old
代中初始化(最小)的大小 (字节) 。 - OGCMX:
Old
代的最大容量 (字节) 。 - OGC:
Old
代当前新生成的容量 (字节) 。 - O:
Old
代已使用的占当前容量百分比。
- S0:年轻代中第一个
Survivor
(幸存区)已使用的占当前容量百分比。 - S1:年轻代中第二个
Survivor
(幸存区)已使用的占当前容量百分比 。 - S0CMX:年轻代中第一个
Survivor
(幸存区)的最大容量 (字节) 。 - S1CMX :年轻代中第二个
Survivor
(幸存区)的最大容量 (字节) 。 - E:年轻代中
Eden
(伊甸园)已使用的占当前容量百分比 。 - ECMX:年轻代中
Eden
(伊甸园)的最大容量 (字节) 。
- DSS:当前需要
Survivor
(幸存区)的容量 (字节)(Eden
区已满)。 - TT: 持有次数限制。
- MTT : 最大持有次数限制。
3. Jstack
jstack
是 JDK
中的一个命令行工具,用于生成 Java
进程的线程转储(thread dump
)。
线程转储是一个描述 Java
虚拟机中所有线程当前状态的快照,包括线程的堆栈信息。
# 查询进程堆栈信息
jstack [options] <pid>
# <line num>: 限制显示行数
# <pid_16>: 进程转十六进制
jstack <pid> | grep -A <line num> <pid_16_hex>
# 转十六进制
printf '%x\n' <pid>
(1) -l
除了线程堆栈外,还显示关于锁的附加信息,这将显示每个锁的拥有者和等待者。
jstack -l <pid>
(2) -F
当进程无响应时,强制生成线程转储,在进程卡死或无响应时非常有用。
jstack -F <pid>
(3) -m
打印 Java
和本地 C/C++
帧的混合堆栈。
jstack -m <pid>
(4) -e
打印线程的锁信息。
jstack -e <pid>
4. JMap
jmap
是 JDK
中的一个命令行工具,用于生成 Java
进程的内存转储快照。
这个快照通常称为 heap dump
,它是 Java
堆内存的详细信息,包括对象的数量、类型、分布等,对于分析内存泄漏和性能问题非常有用。
jmap [options] <pid>
(1) -heap
打印 Java
堆内存的概要信息,包括堆的使用情况和配置。
jmap -heap <pid>
(2) -heap
打印 Java
堆内存中对象的直方图,显示每个类的实例数量和占用的内存大小。
jmap -histo <pid>
(3) -dump
生成堆转储文件,将 Java
堆内存的详细信息保存到指定的文件中。
在线 dump
文件分析工具:https://heaphero.io/index.jsp
。
jmap -dump:file=<export_path> <pid>
# 生成堆栈信息日志
jmap -dump:format=b,file=./dump.bin 123
(4) -F
当进程无响应时,强制生成堆转储,在处理无响应或死锁的情况下很有用。
jmap -F -dump:file=<dumpfile> <pid>
参考文档