JVM - 运行时参数
# 1. JVM 参数选项概述
JVM (Java Virtual Machine) 提供了丰富的命令行参数选项,允许开发者和运维人员在启动和运行 Java 应用程序时,对虚拟机的行为进行配置和调优,包括内存分配、垃圾收集策略、性能监控等方面。了解并合理使用这些参数对于优化应用性能、排查问题至关重要。
官方文档参考 (Java 8):
https://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html
JVM 参数大致可分为三类:标准参数、-X 参数和 -XX 参数。
# 类型一:标准参数选项 (-)
- 特点:
- 相对稳定,未来版本基本不会改变。
- JVM 的各个版本都支持。
- 以单个
-
开头。
- 查看方式: 通过
java -help
命令可以查看所有标准参数。
> java -help
用法: java [-options] class [args...]
(执行类)
或 java [-options] -jar jarfile [args...]
(执行 jar 文件)
其中选项包括:
-d32 使用 32 位数据模型 (如果可用)
-d64 使用 64 位数据模型 (如果可用)
-server 选择 "server" VM
默认 VM 是 server. # 关键信息:指明了 Server 模式是默认
-cp <目录和 zip/jar 文件的类搜索路径>
-classpath <目录和 zip/jar 文件的类搜索路径>
用 ; 分隔的目录, JAR 档案
和 ZIP 档案列表, 用于搜索类文件。 # 指定类路径
-D<名称>=<值>
设置系统属性 # 非常常用,用于设置应用需要的配置
-verbose:[class|gc|jni]
启用详细输出 # 监控类加载、GC、JNI 活动
-version 输出产品版本并退出
-version:<值>
警告: 此功能已过时, 将在
未来发行版中删除。
需要指定的版本才能运行
-showversion 输出产品版本并继续
-jre-restrict-search | -no-jre-restrict-search
警告: 此功能已过时, 将在
未来发行版中删除。
在版本搜索中包括/排除用户专用 JRE
-? -help 输出此帮助消息
-X 输出非标准选项的帮助 # 用于查看 -X 参数
-ea[:<packagename>...|:<classname>]
-enableassertions[:<packagename>...|:<classname>]
按指定的粒度启用断言 # 启用断言,开发测试常用
-da[:<packagename>...|:<classname>]
-disableassertions[:<packagename>...|:<classname>]
禁用具有指定粒度的断言 # 禁用断言
-esa | -enablesystemassertions
启用系统断言
-dsa | -disablesystemassertions
禁用系统断言
-agentlib:<libname>[=<选项>]
加载本机代理库 <libname>, 例如 -agentlib:hprof
另请参阅 -agentlib:jdwp=help 和 -agentlib:hprof=help # 加载 C/C++ Agent
-agentpath:<pathname>[=<选项>]
按完整路径名加载本机代理库
-javaagent:<jarpath>[=<选项>]
加载 Java 编程语言代理, 请参阅 java.lang.instrument # 加载 Java Agent,APM 工具常用
-splash:<imagepath>
使用指定的图像显示启动屏幕
有关详细信息, 请参阅 http://www.oracle.com/technetwork/java/javase/documentation/index.html。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
Server 模式与 Client 模式
HotSpot JVM 提供两种运行模式:Server 模式和 Client 模式。可以通过 -server
和 -client
参数显式指定。
- Client 模式:
- 启动速度较快,占用内存较少。
- 适用于桌面应用程序或对启动性能要求较高的场景。
- 默认使用串行 (Serial) 垃圾收集器。
- 在 32 位 Windows 系统上通常是默认模式。
- Server 模式:
- 启动较慢,但运行时性能和吞吐量更高,因为会进行更多的即时编译 (JIT) 优化。
- 适用于需要长时间运行、对性能要求较高的服务器端应用程序。
- 默认使用并行 (Parallel) 垃圾收集器 (在 JDK 8 中是 Parallel Scavenge + Parallel Old,JDK 9+ 是 G1)。
- 在 64 位系统上通常是唯一支持且默认的模式。需要较好的硬件配置(通常建议至少 2 核 CPU 和 2GB 内存)。
如何查看当前 JVM 的默认模式?
使用 java -version
命令查看。输出中会包含 Client VM
或 Server VM
字样。
> java -version
java version "1.8.0_201"
Java(TM) SE Runtime Environment (build 1.8.0_201-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.201-b09, mixed mode) # 表明是 64 位 Server 模式
2
3
4
# 类型二:-X 参数选项
- 特点:
- 属于非标准化参数 (Non-Standard Options),意味着它们的行为和存在性在不同 JVM 版本之间可能发生变化,但相对
-XX
参数来说更稳定一些。 - 以
-X
开头。
- 属于非标准化参数 (Non-Standard Options),意味着它们的行为和存在性在不同 JVM 版本之间可能发生变化,但相对
- 查看方式: 通过
java -X
命令查看。
> java -X
-Xmixed 混合模式执行 (默认) # JIT 编译模式,见下文
-Xint 仅解释模式执行 # JIT 编译模式
-Xbootclasspath:<用 ; 分隔的目录和 zip/jar 文件>
设置搜索路径以引导类和资源 # 设置 Bootstrap Classpath
-Xbootclasspath/a:<用 ; 分隔的目录和 zip/jar 文件>
附加在引导类路径末尾
-Xbootclasspath/p:<用 ; 分隔的目录和 zip/jar 文件>
置于引导类路径之前
-Xdiag 显示附加诊断消息
-Xnoclassgc 禁用类垃圾收集 # 禁止回收 Class 对象(可能导致 Metaspace/PermGen OOM)
-Xincgc 启用增量垃圾收集 # 已不推荐使用
-Xloggc:<file> 将 GC 状态记录在文件中 (带时间戳) # 指定 GC 日志文件路径,等效于 -XX:LogFile,但已被 -Xlog 替代
-Xbatch 禁用后台编译 # JIT 相关
-Xms<size> 设置初始 Java 堆大小 # 等效于 -XX:InitialHeapSize
-Xmx<size> 设置最大 Java 堆大小 # 等效于 -XX:MaxHeapSize
-Xss<size> 设置 Java 线程堆栈大小 # 等效于 -XX:ThreadStackSize
-Xprof 输出 cpu 配置文件数据 # 分析相关
-Xfuture 启用最严格的检查, 预期将来的默认值
-Xrs 减少 Java/VM 对操作系统信号的使用 (请参阅文档) # 避免 JVM 处理某些 OS 信号
-Xcheck:jni 对 JNI 函数执行其他检查 # 增强 JNI 调用检查
-Xshare:off 不尝试使用共享类数据 # 关闭 CDS (Class Data Sharing)
-Xshare:auto 在可能的情况下使用共享类数据 (默认) # 自动使用 CDS
-Xshare:on 要求使用共享类数据, 否则将失败。 # 强制使用 CDS
-XshowSettings 显示所有设置并继续 # 显示 JVM 设置信息
-XshowSettings:all
显示所有设置并继续
-XshowSettings:vm 显示所有与 vm 相关的设置并继续
-XshowSettings:properties
显示所有属性设置并继续
-XshowSettings:locale
显示所有与区域设置相关的设置并继续
-X 选项是非标准选项, 如有更改, 恕不另行通知。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
JVM 的 JIT 编译模式相关选项:
HotSpot JVM 执行 Java 字节码有以下几种模式:
-Xint
(Interpreted Mode): 禁用 JIT 编译器。所有字节码都通过解释器执行。启动速度快,但运行速度最慢。适用于调试或代码执行次数极少的场景。-Xcomp
(Compiled Mode): 首次执行就强制编译。JVM 在第一次执行字节码时就将其编译成本地机器码,然后执行。运行速度快,但启动速度慢,且可能编译了并不常用的代码。-Xmixed
(Mixed Mode): 默认模式。结合了解释器和 JIT 编译器。开始时由解释器执行,同时 JIT 编译器在后台根据热点探测 (方法调用频率、循环次数等) 有选择地将频繁执行的代码 (“热点代码”) 编译成本地代码,以获得最佳的综合性能(启动速度和运行速度的平衡)。
可以通过 java -version
命令查看当前的模式 (mixed mode
, interpreted mode
或 compiled mode
)。
-Xms
, -Xmx
, -Xss
与 -XX
参数的关系:
这三个常用的 -X
参数实际上是对应 -XX
参数的简写形式,它们的功能完全等价:
-Xms<size>
<=>-XX:InitialHeapSize=<size>
(设置初始堆大小)-Xmx<size>
<=>-XX:MaxHeapSize=<size>
(设置最大堆大小)-Xss<size>
<=>-XX:ThreadStackSize=<size>
(设置线程栈大小)
# 类型三:-XX 参数选项
- 特点:
- 属于非标准化参数,主要用于 JVM 的性能调优和 Debug。
- 是使用最多、功能最强大的一类参数。
- 这类选项通常被认为是实验性 (Experimental) 或 诊断性 (Diagnostic) 的,其行为或名称在不同 JVM 版本间可能发生变化甚至被移除。
- 以
-XX:
开头,后面跟着具体的选项名称。
- 作用: 对 JVM 进行更细粒度的控制,如调整内存区域比例、选择垃圾收集器、开启/关闭特定优化、输出详细日志等。
-XX
参数的两种主要格式:
布尔类型 (Boolean Type):用于启用或禁用某个特性。
- 语法:
-XX:+<option>
: 启用<option>
特性。-XX:-<option>
: 禁用<option>
特性。
- 示例:
-XX:+UseParallelGC
: 启用 Parallel Scavenge + Parallel Old 垃圾收集器组合。-XX:+UseG1GC
: 启用 G1 垃圾收集器。-XX:+UseAdaptiveSizePolicy
: 启用并行收集器的自适应调节策略 (默认开启)。-XX:-UseBiasedLocking
: 禁用偏向锁优化。
- 注意: 某些选项默认是开启的,需要使用
-
来禁用。
- 语法:
键值类型 (Key-Value Type):用于设置某个选项的值(数值或字符串)。
- 数值类型语法:
-XX:<option>=<number>[unit]
<number>
是数值。[unit]
是可选的单位后缀:k
或K
(KB),m
或M
(MB),g
或G
(GB)。如果无单位,通常表示字节 (大小) 或次数/毫秒 (根据参数含义决定)。- 示例:
-XX:NewSize=1024m
: 设置新生代初始大小为 1024MB。-XX:MaxGCPauseMillis=500
: 设置期望的最大 GC 停顿时间为 500 毫秒。-XX:GCTimeRatio=19
: 设置 GC 时间占总时间比例的目标 (GC 时间 / (应用程序时间 + GC 时间) <= 1 / (1 + 19) = 5%)。-XX:NewRatio=2
: 设置老年代与新生代的内存大小比例为 2:1。-XX:SurvivorRatio=8
: 设置 Eden 区与单个 Survivor 区的大小比例为 8:1。
- 字符串类型语法:
-XX:<option>=<string>
- 示例:
-XX:HeapDumpPath=/var/log/jvm/heapdump.hprof
: 指定 OOM 时 Heap Dump 文件的存储路径。-XX:OnError="gcore %p"
: 指定发生致命错误时执行的外部命令。
- 示例:
- 数值类型语法:
查看 -XX
参数:
由于 -XX
参数众多且可能变化,没有一个简单的命令能列出所有参数的说明。但有几个 -XX
参数可以帮助我们查看当前 JVM 使用的 -XX
参数及其值:
-XX:+PrintFlagsInitial
: 打印所有-XX
参数的初始默认值。-XX:+PrintFlagsFinal
: 打印所有-XX
参数在经过命令行指定、配置文件读取、JVM 自适应调整后的最终生效值。如果值与默认值不同,会用:=
标记。这是非常有用的参数,可以查看某个参数的实际值。-XX:+PrintCommandLineFlags
: 打印出在命令行中显式指定或由 JVM 根据环境自动设置的-XX
参数。
解锁隐藏参数:
默认情况下,PrintFlagsFinal
等命令不会显示所有参数,特别是那些被标记为 Diagnostic (诊断性) 或 Experimental (实验性) 的参数。要查看这些参数,需要先解锁:
-XX:+UnlockDiagnosticVMOptions
: 解锁诊断性参数。-XX:+UnlockExperimentalVMOptions
: 解锁实验性参数。
例如,要查看所有参数(包括诊断和实验性)的最终值,可以使用:
java -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+PrintFlagsFinal -version
# 2. 如何添加 JVM 参数选项
有多种方式可以向 Java 应用程序传递 JVM 参数:
命令行运行
.jar
文件时添加: 在java
命令和-jar
参数之间添加 JVM 参数。# 设置初始堆100m,最大堆100m,打印详细GC日志及时间戳 java -Xms100m -Xmx100m -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -jar your-app.jar
1
2在 IDE (Eclipse/IntelliJ IDEA) 中配置: 在运行/调试配置 (Run/Debug Configurations) 中,找到 "VM options" 或类似的输入框,填入所需的 JVM 参数,多个参数用空格分隔。
Web 服务器 (如 Tomcat) 中添加:
Tomcat on Linux/macOS: 编辑
bin/catalina.sh
文件,找到JAVA_OPTS
或CATALINA_OPTS
变量,在引号内添加参数。JAVA_OPTS
对所有 Java 命令生效 (start, stop, run),CATALINA_OPTS
仅对start
和run
生效。# 编辑 catalina.sh # 建议使用 CATALINA_OPTS,避免影响 stop 等操作 CATALINA_OPTS="$CATALINA_OPTS -Xms512m -Xmx1024m -XX:+UseG1GC" export CATALINA_OPTS
1
2
3
4Tomcat on Windows: 编辑
bin/catalina.bat
文件,找到set "JAVA_OPTS=..."
或set "CATALINA_OPTS=..."
的行,添加参数。rem 编辑 catalina.bat set "CATALINA_OPTS=%CATALINA_OPTS% -Xms512m -Xmx1024m -XX:+UseG1GC"
1
2
程序运行时动态添加/修改 (通过
jinfo
): 对于可管理 (manageable) 的-XX
参数,可以使用jinfo
命令在运行时动态修改。# 动态启用 PrintGCDetails (假设它是 manageable 的) jinfo -flag +PrintGCDetails <pid> # 动态禁用 PrintGCDetails jinfo -flag -PrintGCDetails <pid> # 动态设置 GCTimeRatio (假设它是 manageable 的) jinfo -flag GCTimeRatio=24 <pid>
1
2
3
4
5
6
7
8注意: 这种方式能力有限,只有少数参数支持运行时修改,且修改可能不会持久生效(重启后恢复)。
# 3. 常用的 JVM 参数选项详解
以下列出一些常用的 JVM 参数,按功能分类:
# 打印 JVM 参数与设置值
这些参数用于了解 JVM 当前的配置情况。
-XX:+PrintCommandLineFlags
: 打印出最终生效的、由用户或 JVM 设置的-XX
参数。非常有用,可以快速看到关键配置。-XX:+PrintFlagsInitial
: 打印所有-XX
参数的默认值。用于对比默认行为。-XX:+PrintFlagsFinal
: 打印所有-XX
参数的最终生效值。用于确认参数是否按预期设置,以及查看 JVM 自适应调整后的结果。推荐与Unlock...Options
结合使用。-XX:+PrintVMOptions
: 打印传递给 JVM 的参数(以-XX:
开头的)。
# 内存区域大小设置
栈内存 (Stack)
-Xss<size>
或-XX:ThreadStackSize=<size>
: 设置每个线程的栈大小。- 示例:
-Xss256k
(设置栈大小为 256 KB)。 - 默认值取决于平台(Linux/macOS 通常 1MB,Windows 可能不同)。
- 栈空间太小可能导致
StackOverflowError
(尤其在递归深或方法调用链长时)。 - 栈空间太大则会减少可创建的线程数(因为总虚拟内存有限)。
- 示例:
堆内存 (Heap)
-Xms<size>
或-XX:InitialHeapSize=<size>
: 设置 JVM 初始堆大小。- 示例:
-Xms2048m
(初始堆 2GB)。 - JVM 启动时即分配这么多内存。
- 示例:
-Xmx<size>
或-XX:MaxHeapSize=<size>
: 设置 JVM 最大堆大小。- 示例:
-Xmx4096m
(最大堆 4GB)。 - 这是 JVM 能使用的堆内存上限。
- 最佳实践: 通常建议将
-Xms
和-Xmx
设置为相同的值,以避免运行时堆内存动态伸缩带来的性能开销和 Full GC。
- 示例:
-Xmn<size>
或-XX:NewSize=<size> -XX:MaxNewSize=<size>
: 直接设置年轻代的大小。- 示例:
-Xmn1g
(年轻代大小固定为 1GB)。 - 设置年轻代大小会影响 Minor GC 的频率和持续时间。
- 示例:
-XX:NewRatio=<ratio>
: 设置老年代与年轻代的内存大小比例。- 示例:
-XX:NewRatio=2
(表示老年代:年轻代 = 2:1)。默认值为 2。 - 如果同时设置了
-Xmn
,则-NewRatio
会失效。
- 示例:
-XX:SurvivorRatio=<ratio>
: 设置新生代中 Eden 区与单个 Survivor 区的大小比例。- 示例:
-XX:SurvivorRatio=8
(表示 Eden:S0:S1 = 8:1:1)。默认值为 8。 - 注意:这个比例是 Eden / Survivor。Survivor 有两个,所以总的年轻代比例是
Eden + S0 + S1
。
- 示例:
-XX:+UseAdaptiveSizePolicy
: 启用自适应大小策略 (Parallel GC 默认开启)。- JVM 会根据运行情况动态调整年轻代大小、Survivor 比例、晋升年龄等参数,以尝试达到用户设定的吞吐量和停顿时间目标 (
GCTimeRatio
,MaxGCPauseMillis
)。 - 开启后,手动设置的
-XX:NewRatio
,-XX:SurvivorRatio
等可能失效。
- JVM 会根据运行情况动态调整年轻代大小、Survivor 比例、晋升年龄等参数,以尝试达到用户设定的吞吐量和停顿时间目标 (
-XX:PretenureSizeThreshold=<bytes>
: 大对象直接晋升老年代阈值。- 单位是字节。如果一个对象的大小超过这个阈值,将直接在老年代分配。
- 默认值通常是 0 (表示不启用)。
- 只对 Serial 和 ParNew 收集器有效。Parallel Scavenge 和 G1 有自己的策略。
- 用于避免大对象在 Eden 区和 Survivor 区之间频繁复制。
-XX:MaxTenuringThreshold=<age>
: 对象晋升老年代的最大年龄。- 对象在 Survivor 区每熬过一次 Minor GC,年龄就加 1。当年龄达到此阈值时,会被晋升到老年代。
- 默认值为 15 (最大值)。CMS 收集器下默认是 6。
- 如果设置为 0,对象创建后首次 Minor GC 就会直接晋升老年代 (如果 Survivor 空间足够)。
- 调小此值会让对象更快进入老年代,减少 Young GC 时间,但可能增加 Old GC 压力。
-XX:+PrintTenuringDistribution
: 在每次 Minor GC 后打印 Survivor 区中对象的年龄分布。用于观察对象晋升情况,辅助调整MaxTenuringThreshold
。-XX:TargetSurvivorRatio=<percent>
: Survivor 区域期望占用比例 (默认 50)。- 在 Minor GC 后,JVM 会计算将存活对象放入 To Survivor 区后,该区的占用率。如果占用率超过此百分比,JVM 可能会动态调整
MaxTenuringThreshold
,让年龄小于阈值的对象也提前晋升,以避免 To Survivor 区溢出。
- 在 Minor GC 后,JVM 会计算将存活对象放入 To Survivor 区后,该区的占用率。如果占用率超过此百分比,JVM 可能会动态调整
方法区/元空间 (Method Area / Metaspace) (JDK 8+) 或 永久代 (Permanent Generation) (JDK 7及之前)
-XX:MetaspaceSize=<size>
(JDK 8+): 设置元空间初始大小。达到此值会触发 Full GC (类型是 Metadata GC Threshold)。- 示例:
-XX:MetaspaceSize=256m
- 示例:
-XX:MaxMetaspaceSize=<size>
(JDK 8+): 设置元空间最大大小。默认无上限(受限于物理内存)。如果设置了,达到此值会 OOM。- 示例:
-XX:MaxMetaspaceSize=512m
- 示例:
-XX:PermSize=<size>
(JDK 7及之前): 设置永久代初始大小。-XX:MaxPermSize=<size>
(JDK 7及之前): 设置永久代最大大小。-XX:+UseCompressedOops
: 启用压缩对象指针 (Compressed Ordinary Object Pointers)。- 在 64 位 JVM 上,通过优化指针表示,用 32 位指针模拟 64 位指针的功能(在堆小于约 32GB 时),可以节省大量内存。默认开启 (需要 CPU 支持)。
-XX:+UseCompressedClassPointers
: 启用压缩类指针 (Compressed Class Pointers)。- 压缩存储在对象头中的指向类元数据的指针。默认开启。
-XX:CompressedClassSpaceSize=<size>
: 设置用于存储压缩类指针的类元空间 (Klass Metaspace) 的大小。- 默认大小约为 1GB。如果加载大量类导致这部分空间不足,即使整体 Metaspace 未满也可能触发 Full GC 或 OOM。可以适当调大此值。
直接内存 (Direct Memory)
-XX:MaxDirectMemorySize=<size>
: 设置堆外直接内存的最大可用大小。- NIO 的
DirectByteBuffer
使用此内存。 - 默认值等于 Java 堆的最大值 (
-Xmx
)。 - 如果应用大量使用 NIO 直接内存,需要根据实际情况调整此值,否则可能导致直接内存 OOM (表现形式可能与堆 OOM 不同)。
- NIO 的
# OutOfMemoryError 相关选项
这些参数用于在发生 OOM 时辅助诊断。
-XX:+HeapDumpOnOutOfMemoryError
: 在抛出OutOfMemoryError
时,自动生成 Heap Dump 文件。强烈推荐在生产环境开启。-XX:+HeapDumpBeforeFullGC
: 在执行 Full GC 之前生成 Heap Dump 文件。用于分析 Full GC 前的内存状况,但会增加 Full GC 的停顿时间。与上一个参数通常互斥。-XX:HeapDumpPath=<path/to/filename.hprof>
: 指定 Heap Dump 文件的生成路径和文件名。可以使用%p
代表进程 ID。- 示例:
-XX:HeapDumpPath=/data/dumps/java_%p.hprof
- 默认生成在 JVM 的启动目录下,文件名为
java_pid<pid>.hprof
。
- 示例:
-XX:OnOutOfMemoryError=<command>
: 指定当 JVM 抛出OutOfMemoryError
后要执行的外部命令或脚本。- 示例:
-XX:OnOutOfMemoryError="kill -9 %p"
(危险操作,直接杀死进程) 或-XX:OnOutOfMemoryError="/path/to/script.sh %p"
(执行自定义脚本,如发送告警、记录信息等)。
- 示例:
# 垃圾收集器 (GC) 相关选项
选择和配置垃圾收集器是 JVM 调优的核心内容之一。
垃圾收集器搭配关系 (重要概念): 不同的垃圾收集器负责不同的内存区域(年轻代/老年代),并且它们之间存在固定的搭配关系。
图:垃圾收集器组合关系 (注意图中 Deprecated/Removed 的标记)
- 红色虚线: JDK 8 中已 Deprecated (不推荐使用),JDK 9 中已移除。例如 ParNew + Serial Old,CMS + Serial Old (作为 Full GC 备用方案)。
- 绿色虚线: JDK 14 中已 Deprecated。
- 绿色虚框: JDK 9 中已 Deprecated,JDK 14 中已移除 (CMS)。
查看默认使用的垃圾收集器:
- 方法一:使用
-XX:+PrintCommandLineFlags
参数启动 JVM,输出中会包含使用的 GC 相关参数。 - 方法二:对于运行中的进程,使用
jinfo -flag <GC相关参数> <pid>
查看对应参数是否启用。例如jinfo -flag UseParallelGC <pid>
。
常用 GC 相关参数:
1. Serial GC (串行收集器)
- 年轻代: Serial Collector
- 老年代: Serial Old Collector
- 特点: 单线程执行 GC,简单高效 (在单核或内存小的场景下),但 STW (Stop-The-World) 时间可能较长。Client 模式默认。
- 启用参数:
-XX:+UseSerialGC
(同时启用 Serial + Serial Old)。
2. ParNew GC (并行新生代收集器)
- 年轻代: ParNew Collector (Serial 的多线程版本)
- 特点: 多线程进行 Minor GC,可以配合 CMS 使用。
- 启用参数:
-XX:+UseParNewGC
: 明确指定年轻代使用 ParNew。如果使用 CMS (-XX:+UseConcMarkSweepGC
),ParNew 会被自动启用。-XX:ParallelGCThreads=<N>
: 设置 ParNew (以及 Parallel Scavenge) 的 GC 线程数。默认与 CPU 核数相关。通常设置为 CPU 核数。
3. Parallel GC (并行收集器 - 吞吐量优先)
- 年轻代: Parallel Scavenge Collector
- 老年代: Parallel Old Collector
- 特点: 关注吞吐量 (CPU 用于运行用户代码的时间 / 总时间)。多线程执行 GC,STW 时间相对较长,但单位时间内完成的 GC 工作多。JDK 8 Server 模式默认。
- 启用参数:
-XX:+UseParallelGC
: 启用 Parallel Scavenge (年轻代)。会自动启用 Parallel Old (老年代)。-XX:+UseParallelOldGC
: 启用 Parallel Old (老年代)。会自动启用 Parallel Scavenge (年轻代)。这两个参数通常是互相激活的。
- 相关调优参数:
-XX:ParallelGCThreads=<N>
: 设置 GC 线程数 (同 ParNew)。-XX:MaxGCPauseMillis=<milliseconds>
: 设置期望的最大 STW 停顿时间。JVM 会尝试调整堆大小、分代比例等来满足这个目标,但这只是一个目标,不保证一定能达到。设置过低可能导致 GC 频繁,反而降低吞吐量。-XX:GCTimeRatio=<N>
: 设置吞吐量目标。GC 时间占总时间的比例目标是1 / (1 + N)
。默认值 99,即目标 GC 时间占比不超过 1%。MaxGCPauseMillis
和GCTimeRatio
往往是相互矛盾的,需要权衡。-XX:+UseAdaptiveSizePolicy
: 启用自适应调节策略 (默认开启)。让 JVM 根据MaxGCPauseMillis
和GCTimeRatio
目标自动调整内存布局。开启后,手动设置的分代比例、晋升阈值等可能失效。
4. CMS GC (Concurrent Mark Sweep - 低延迟)
- 年轻代: 通常搭配 ParNew
- 老年代: CMS Collector
- 特点: 关注低停顿时间。大部分标记 (Mark) 和清除 (Sweep) 阶段可以与用户线程并发执行,显著减少 STW 时间。但存在内存碎片问题,且并发阶段会占用 CPU 资源。
- 状态: JDK 9 已 Deprecated,JDK 14 已移除。不推荐在新项目中使用。
- 启用参数:
-XX:+UseConcMarkSweepGC
: 启用 CMS。会自动启用-XX:+UseParNewGC
。
- 相关调优参数:
-XX:CMSInitiatingOccupancyFraction=<percent>
: 设置老年代使用率达到多少百分比时触发 CMS GC。默认值在 JDK 6 后是 92%。调低可以更早触发 GC,避免 OOM 或退化为 Serial Old Full GC,但会增加 GC 频率。-XX:+UseCMSInitiatingOccupancyOnly
: 禁用动态调整触发阈值,始终使用-XX:CMSInitiatingOccupancyFraction
设定的值。-XX:+UseCMSCompactAtFullCollection
: 在 Full GC (通常是 CMS 失败或碎片过多时退化执行的 Serial Old Full GC) 后进行内存碎片整理。默认开启。碎片整理会导致较长的 STW。-XX:CMSFullGCsBeforeCompaction=<N>
: 设置执行 N 次不进行碎片整理的 Full GC 后,再执行一次带碎片整理的 Full GC。默认 0,表示每次都整理。-XX:ParallelCMSThreads=<N>
或-XX:ConcGCThreads=<N>
: 设置 CMS 并发阶段使用的线程数。-XX:+CMSScavengeBeforeRemark
: 在 CMS 重新标记 (Remark) 阶段前强制进行一次 Minor GC,可以减少 Remark 阶段需要扫描的对象,缩短 STW 时间。
5. G1 GC (Garbage-First - 平衡吞吐量与低延迟)
- 特点: JDK 9 及以后的默认 GC。将堆划分为多个大小相等的 Region,年轻代和老年代不再是物理上连续的。可以预测停顿时间模型。在大部分场景下能提供比 CMS 更好的性能和更可控的停顿时间。适用于大内存 (通常 >= 4GB) 应用。
- 启用参数:
-XX:+UseG1GC
: 启用 G1 GC。
- 相关调优参数:
-XX:G1HeapRegionSize=<size>
: 设置每个 Region 的大小。必须是 2 的幂次方,范围 1MB 到 32MB。如果不设置,JVM 会根据堆大小自动计算(目标是约 2048 个 Region)。调整 Region 大小会影响大对象的分配和 GC 效率。-XX:MaxGCPauseMillis=<milliseconds>
: 设置期望的最大 GC 停顿时间 (目标值)。这是 G1 调优的核心参数。G1 会尽力调整年轻代大小、GC 频率等来满足这个目标。默认值 200ms。设置太低可能导致吞吐量下降。-XX:ParallelGCThreads=<N>
: 设置 STW 阶段 (如 Minor GC, Remark) 的并行 GC 线程数。-XX:ConcGCThreads=<N>
: 设置并发标记阶段的线程数。通常是ParallelGCThreads
的 1/4 左右。-XX:InitiatingHeapOccupancyPercent=<percent>
(IHOP): 设置整个堆内存使用率达到多少百分比时,触发并发标记周期。默认 45%。如果并发标记赶不上对象分配速度,可能导致 Full GC。可以根据老年代增长速度调整此值。-XX:G1NewSizePercent=<percent>
: 年轻代占整个堆的最小百分比。默认 5%。-XX:G1MaxNewSizePercent=<percent>
: 年轻代占整个堆的最大百分比。默认 60%。G1 会在这两个百分比之间动态调整年轻代大小以满足停顿时间目标。-XX:G1ReservePercent=<percent>
: 保留空闲区域百分比。为防止并发回收过程中 Survivor 区或老年代空间不足(晋升失败),G1 会保留一部分堆内存不分配。默认 10%。如果出现 To-space Exhausted,可以适当调大此值。
如何选择垃圾收集器?
- 简单原则:
- 低内存/单核/客户端: Serial GC (
-XX:+UseSerialGC
)。 - 高吞吐量/后台计算/可接受较长 STW: Parallel GC (
-XX:+UseParallelGC
)。 - 低延迟/快速响应/交互式应用: G1 GC (
-XX:+UseG1GC
)。CMS 已不推荐。
- 低内存/单核/客户端: Serial GC (
- 现代应用首选: G1 GC 是目前的主流选择,尤其对于服务端和大内存应用,通常能取得良好的平衡。
- JVM 自适应: 可以先不设置复杂的 GC 参数,只设置堆大小 (
-Xms
,-Xmx
) 和 JDK 版本(如使用 JDK 11+ 默认启用 G1),让 JVM 自适应运行,观察性能。 - 特定场景调优: 如果默认配置性能不佳,再根据监控数据(GC 日志、JFR 等)和具体性能瓶颈(高延迟?低吞吐量?),针对性地调整 GC 参数(主要是 G1 的
MaxGCPauseMillis
和 IHOP)。 - 没有银弹: 没有绝对最好的收集器,选择和调优需要结合具体应用场景、硬件配置和性能需求。
# GC 日志相关选项
输出详细的 GC 日志是分析 GC 性能、排查内存问题的关键。
-verbose:gc
或-XX:+PrintGC
: 打印简要 GC 信息 (每次 GC 的类型、耗时、内存变化)。信息量较少。-XX:+PrintGCDetails
: 打印详细 GC 日志。包含每次 GC 前后各分代/区域的容量、使用量变化,GC 原因,耗时等。强烈推荐开启。-XX:+PrintGCTimeStamps
: 在详细 GC 日志 (PrintGCDetails
) 的每行开头添加相对于 JVM 启动的时间戳 (秒)。-XX:+PrintGCDateStamps
: 在详细 GC 日志 (PrintGCDetails
) 的每行开头添加绝对日期时间戳。推荐与PrintGCTimeStamps
一起使用,方便关联日志。-Xloggc:<filepath>
或 (JDK 9+ 更推荐的-Xlog:gc*:<filepath>:...
): 将 GC 日志输出到指定文件。避免 GC 日志与应用日志混合。- 示例:
-Xloggc:/var/log/myapp/gc.log
- 示例:
-XX:+PrintHeapAtGC
: 在每次 GC 之前和之后打印详细的堆内存信息(各区使用情况)。日志量会很大,通常用于调试。图:PrintHeapAtGC 输出示例
-XX:+TraceClassLoading
: 监控并打印类的加载信息。-XX:+PrintGCApplicationStoppedTime
: 打印每次 GC 导致的应用程序停顿 (STW) 时间。-XX:+PrintGCApplicationConcurrentTime
: 打印应用程序在两次 GC 停顿之间的并发执行时间。-XX:+PrintReferenceGC
: 打印关于不同类型引用 (软引用、弱引用、虚引用、终结器引用) 处理的 GC 信息。-XX:+PrintTenuringDistribution
: 打印每次 Minor GC 后 Survivor 区对象的年龄分布。-XX:+UseGCLogFileRotation
: 启用 GC 日志文件滚动。防止单个日志文件过大。-XX:NumberOfGCLogFiles=<N>
: 设置滚动日志文件的数量。当达到数量限制后,会覆盖最早的文件。-XX:GCLogFileSize=<size>
: 设置每个滚动 GC 日志文件的最大大小。
# 其他常用参数
-XX:+DisableExplicitGC
: 禁用对System.gc()
调用的响应。即应用代码调用System.gc()
时,JVM 会忽略它,不会触发 Full GC。推荐在生产环境开启,避免代码误用导致不必要的 Full GC。-XX:ReservedCodeCacheSize=<size>
: 设置 JIT 代码缓存 (Code Cache) 的最大大小。存储编译后的本地代码。-XX:InitialCodeCacheSize=<size>
: 设置代码缓存的初始大小。-XX:+UseCodeCacheFlushing
: 当代码缓存接近满时,允许 JVM 丢弃一些旧的或不常用的编译代码。避免因代码缓存耗尽而切换到纯解释模式。-XX:+DoEscapeAnalysis
: 启用逃逸分析 (默认开启)。JVM 分析对象的作用域,如果对象不会“逃逸”出方法或线程,可能进行栈上分配或标量替换优化,减少堆分配和 GC 压力。-XX:+UseBiasedLocking
: 启用偏向锁 (默认开启,但在高竞争环境下可能带来负面影响,JDK 15 已默认禁用)。一种锁优化,减少无竞争情况下的锁开销。-XX:+UseLargePages
: 启用操作系统的大页内存支持。可以减少 TLB Miss,提高内存访问性能,但需要操作系统配置支持。-XX:+PrintTLAB
: 打印 TLAB (Thread Local Allocation Buffer) 的使用情况。TLAB 是为了加速对象分配,在 Eden 区为每个线程预分配的一小块私有内存。-XX:TLABSize=<size>
: 设置 TLAB 的大小。
# 4. JVM 所有参数总结表
(这里保留并整理原文档的总结表,使其更清晰)
打印设置的 XX 选项及值
指令 | 作用 |
---|---|
-XX:+PrintCommandLineFlags | 打印生效的命令行-XX 参数 |
-XX:+PrintFlagsInitial | 打印所有-XX 参数的默认值 |
-XX:+PrintFlagsFinal | 打印所有-XX 参数的最终生效值 |
-XX:+PrintVMOptions | 打印传递给JVM的-XX 参数 |
内存大小设置
区域 | 指令 | 作用 |
---|---|---|
栈 | -Xss<size> 或 -XX:ThreadStackSize=<size> | 设置线程栈大小 |
堆 | -Xms<size> 或 -XX:InitialHeapSize=<size> | 设置初始堆大小 |
-Xmx<size> 或 -XX:MaxHeapSize=<size> | 设置最大堆大小 | |
-Xmn<size> 或 -XX:NewSize/MaxNewSize | 设置年轻代大小 | |
-XX:NewRatio=<ratio> | 设置老年代/年轻代比例 | |
-XX:SurvivorRatio=<ratio> | 设置 Eden/Survivor 比例 | |
-XX:+UseAdaptiveSizePolicy | 启用堆内存自适应调节 (Parallel GC) | |
-XX:PretenureSizeThreshold=<bytes> | 大对象直接晋升老年代阈值 (Serial/ParNew) | |
-XX:MaxTenuringThreshold=<age> | 对象晋升老年代的最大年龄 | |
-XX:TargetSurvivorRatio=<percent> | Survivor区期望占用比例 | |
方法区 | -XX:MetaspaceSize=<size> | (JDK 8+) 元空间初始大小 |
-XX:MaxMetaspaceSize=<size> | (JDK 8+) 元空间最大大小 | |
-XX:PermSize=<size> | (JDK <=7) 永久代初始大小 | |
-XX:MaxPermSize=<size> | (JDK <=7) 永久代最大大小 | |
-XX:+UseCompressedOops | 启用压缩对象指针 | |
-XX:+UseCompressedClassPointers | 启用压缩类指针 | |
-XX:CompressedClassSpaceSize=<size> | 设置压缩类空间大小 | |
直接内存 | -XX:MaxDirectMemorySize=<size> | 设置NIO直接内存最大值 |
OutOfMemory 相关选项
指令 | 作用 |
---|---|
-XX:+HeapDumpOnOutOfMemoryError | OOM 时生成 Heap Dump |
-XX:+HeapDumpBeforeFullGC | Full GC 前生成 Heap Dump (与上一个互斥) |
-XX:HeapDumpPath=<path> | 指定 Heap Dump 文件路径 |
-XX:OnOutOfMemoryError=<command> | OOM 时执行外部命令 |
垃圾收集器相关选项 (部分)
收集器 | 启用参数 | 主要调优参数 |
---|---|---|
Serial | -XX:+UseSerialGC | (通常无需调优) |
ParNew | -XX:+UseParNewGC | -XX:ParallelGCThreads |
Parallel | -XX:+UseParallelGC | -XX:ParallelGCThreads , -XX:MaxGCPauseMillis , -XX:GCTimeRatio , -XX:+UseAdaptiveSizePolicy |
CMS | -XX:+UseConcMarkSweepGC | -XX:CMSInitiatingOccupancyFraction , -XX:CMSFullGCsBeforeCompaction (JDK <=13) |
G1 | -XX:+UseG1GC | -XX:MaxGCPauseMillis , -XX:InitiatingHeapOccupancyPercent , -XX:G1HeapRegionSize |
GC 日志相关选项 (部分)
指令 | 作用 |
---|---|
-XX:+PrintGCDetails | 打印详细 GC 日志 |
-XX:+PrintGCDateStamps | 添加日期时间戳到 GC 日志 |
-XX:+PrintGCTimeStamps | 添加 JVM 启动时间戳到 GC 日志 |
-Xloggc:<filepath> | 将 GC 日志输出到文件 |
-XX:+UseGCLogFileRotation | 启用 GC 日志滚动 |
-XX:NumberOfGCLogFiles=<N> | 滚动日志文件数量 |
-XX:GCLogFileSize=<size> | 每个滚动日志文件的大小 |
-XX:+PrintTenuringDistribution | 打印 Survivor 区对象年龄分布 |
其他常用选项 (部分)
指令 | 作用 |
---|---|
-XX:+DisableExplicitGC | 禁用 System.gc() 调用 |
-XX:+DoEscapeAnalysis | 启用逃逸分析 (默认开启) |
-XX:+UseBiasedLocking | 启用偏向锁 (JDK 15+ 默认禁用) |
-D<name>=<value> | 设置系统属性 |
-javaagent:<jarpath> | 加载 Java Agent |
# 5. 通过 Java 代码获取 JVM 参数
除了通过命令行参数配置和 jinfo
查看,我们还可以在 Java 代码中通过 java.lang.management
API 来获取 JVM 的运行时信息,这对于应用内部监控非常有用。
ManagementFactory
类: 提供了获取各种 MXBean (管理接口 Bean) 的静态方法,用于访问 JVM 的不同管理方面(内存、线程、GC、类加载等)。Runtime
类: 提供了与运行时环境交互的方法,包括获取内存信息、CPU 核心数等。
示例代码 (MemoryMonitor.java
):
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
/**
* 使用 Java Management API 获取 JVM 内存信息的示例。
*/
public class MemoryMonitor {
public static void main(String[] args) {
// 1. 使用 ManagementFactory 获取 MemoryMXBean
MemoryMXBean memorymbean = ManagementFactory.getMemoryMXBean();
// 2. 获取堆内存使用情况 (HeapMemoryUsage)
MemoryUsage heapUsage = memorymbean.getHeapMemoryUsage();
System.out.println("========= 通过 ManagementFactory 获取堆内存信息 =========");
// getInit(): 返回 JVM 启动时初始分配的堆内存(对应 -Xms),单位:字节
System.out.println("INIT HEAP: " + heapUsage.getInit() / 1024 / 1024 + "m");
// getMax(): 返回最大可用堆内存(对应 -Xmx),单位:字节。如果未设置,可能返回 -1 或一个非常大的值。
System.out.println("MAX HEAP: " + heapUsage.getMax() / 1024 / 1024 + "m");
// getUsed(): 返回当前已使用的堆内存,单位:字节。
System.out.println("USED HEAP: " + heapUsage.getUsed() / 1024 / 1024 + "m");
// getCommitted(): 返回当前已提交(分配给JVM)的堆内存,单位:字节。committed >= used。
System.out.println("COMMITTED HEAP: " + heapUsage.getCommitted() / 1024 / 1024 + "m");
// 打印更详细的内存使用对象信息
System.out.println("\nFull Heap Memory Usage Info:");
System.out.println(heapUsage); // 输出 MemoryUsage 对象的详细信息
// 3. 获取非堆内存使用情况 (NonHeapMemoryUsage),主要包含 Metaspace/PermGen 和 Code Cache
MemoryUsage nonHeapUsage = memorymbean.getNonHeapMemoryUsage();
System.out.println("\nFull Non-Heap Memory Usage Info:");
System.out.println(nonHeapUsage);
// 4. 使用 Runtime 类获取内存信息 (提供不同的视角)
System.out.println("\n========= 通过 Runtime 获取内存信息 =========");
Runtime runtime = Runtime.getRuntime();
// totalMemory(): 返回 JVM 当前已从操作系统申请到的总内存(约等于堆的 Committed + 部分非堆),单位:字节。会随堆伸缩变化。
System.out.println("当前已申请总内存 (totalMemory): " + runtime.totalMemory() / 1024 / 1024 + "m");
// freeMemory(): 返回 JVM 当前已申请到的总内存中的**空闲**部分,单位:字节。 freeMemory = totalMemory - (当前 Heap Used + 可能的其他内部占用)。
System.out.println("当前空闲内存 (freeMemory): " + runtime.freeMemory() / 1024 / 1024 + "m");
// maxMemory(): 返回 JVM 可尝试使用的最大内存量(约等于 -Xmx),单位:字节。
System.out.println("JVM 可用的最大内存 (maxMemory): " + runtime.maxMemory() / 1024 / 1024 + "m");
// 5. 获取 CPU 核心数
System.out.println("\n========= CPU 信息 =========");
System.out.println("可用处理器数量 (availableProcessors): " + runtime.availableProcessors());
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
通过这些 API,可以在应用程序内部实现对 JVM 资源的监控,并根据需要进行报警或动态调整(例如,调整缓存大小、限流等)。