1. 概述
配置 Java 应用时,我们经常需要在不修改启动脚本的情况下传递 JVM 选项。与其每次手动在 java
命令中添加参数,不如使用环境变量 JDK_JAVA_OPTIONS
或 JAVA_TOOL_OPTIONS
。这两个环境变量都能动态传递 JVM 选项,但工作机制截然不同。
本文将通过实际示例对比它们的差异,帮你搞清楚何时该用哪个,以及高效管理 JVM 配置的最佳实践。
2. 什么是 JDK_JAVA_OPTIONS 和 JAVA_TOOL_OPTIONS?
这两个环境变量都能全局指定 JVM 选项,避免每次执行 java
、javac
、javadoc
等 JDK 工具时重复配置。
- ✅
JAVA_TOOL_OPTIONS
:Java 5 引入的老牌选手 - ✅
JDK_JAVA_OPTIONS
:Java 9 推出的新贵
先创建一个测试类:
package com.baeldung;
/**
* 打印环境变量值的简单类
*/
public class TestEnvVar {
public static void main (String[] args){
System.out.println("var1 = '" + System.getProperty("var1") + "'");
System.out.println("var2 = '" + System.getProperty("var2") + "'");
}
}
编译代码:
$ javac com/baeldung/TestEnvVar.java
文件结构:
$ tree
.
└── com
└── baeldung
├── TestEnvVar.class
└── TestEnvVar.java
3 directories, 1 file
本文所有示例基于 Java 23(Linux 环境):
$ java -version
openjdk version "23.0.1" 2024-10-15
OpenJDK Runtime Environment Homebrew (build 23.0.1)
OpenJDK 64-Bit Server VM Homebrew (build 23.0.1, mixed mode, sharing)
3. 执行 Java 启动器——java 命令
最典型的 Java 程序启动方式是使用 java
命令。先通过 JDK_JAVA_OPTIONS
传递参数:
$ JDK_JAVA_OPTIONS="-Dvar1='Hello (JDK_JAVA_OPTIONS)' -Dvar2='World (JDK_JAVA_OPTIONS)'" java com.baeldung.TestEnvVar
NOTE: Picked up JDK_JAVA_OPTIONS: -Dvar1='Hello (JDK_JAVA_OPTIONS)' -Dvar2='World (JDK_JAVA_OPTIONS)'
var1 = 'Hello (JDK_JAVA_OPTIONS)'
var2 = 'World (JDK_JAVA_OPTIONS)'
关键点:
- ✅
java
命令会读取JDK_JAVA_OPTIONS
- ✅ 输出中明确提示已拾取该变量
- ⚠️ 注意:环境变量和命令在同一行执行,确保仅对本次命令生效
执行后检查变量值:
$ echo $JDK_JAVA_OPTIONS
再测试 JAVA_TOOL_OPTIONS
:
$ JAVA_TOOL_OPTIONS="-Dvar1='Hi (JAVA_TOOL_OPTIONS)' -Dvar2='There (JAVA_TOOL_OPTIONS)'" java com.baeldung.TestEnvVar
Picked up JAVA_TOOL_OPTIONS: -Dvar1='Hi (JAVA_TOOL_OPTIONS)' -Dvar2='There (JAVA_TOOL_OPTIONS)'
var1 = 'Hi (JAVA_TOOL_OPTIONS)'
var2 = 'There (JAVA_TOOL_OPTIONS)'
同样生效,但输出提示不同。现在测试冲突场景——当两个变量同时存在时:
$ JAVA_TOOL_OPTIONS="-Dvar1='Hi (JAVA_TOOL_OPTIONS)' -Dvar2='There (JAVA_TOOL_OPTIONS)'" JDK_JAVA_OPTIONS="-Dvar2='World (by JDK_JAVA_OPTIONS)'" java com.baeldung.TestEnvVar
NOTE: Picked up JDK_JAVA_OPTIONS: -Dvar2='World (by JDK_JAVA_OPTIONS)'
Picked up JAVA_TOOL_OPTIONS: -Dvar1='Hi (JAVA_TOOL_OPTIONS)' -Dvar2='There (JAVA_TOOL_OPTIONS)'
var1 = 'Hi (JAVA_TOOL_OPTIONS)'
var2 = 'World (by JDK_JAVA_OPTIONS)'
结论:
- ✅ 两个变量都会被读取
- ✅ 冲突时
JDK_JAVA_OPTIONS
优先级更高(var2
的值被覆盖)
4. 其他 Java 命令
除了 java
,JDK 还提供 javac
、javadoc
等工具。它们如何处理这两个变量?以 javac
为例:
先查看默认堆内存(MaxHeapSize):
$ javac -J-XX:+PrintCommandLineFlags com/baeldung/TestEnvVar.java
... -XX:MaxHeapSize=9663676416 ...
默认值约 9GiB。通过 JAVA_TOOL_OPTIONS
限制为 10MiB:
$ JAVA_TOOL_OPTIONS="-Xmx10m" javac -J-XX:+PrintCommandLineFlags com/baeldung/TestEnvVar.java
Picked up JAVA_TOOL_OPTIONS: -Xmx10m
... -XX:MaxHeapSize=10485760 ...
✅ 生效!堆内存被限制为 10MiB。再用 JDK_JAVA_OPTIONS
测试:
$ JDK_JAVA_OPTIONS="-Xmx10m" javac -J-XX:+PrintCommandLineFlags com/baeldung/TestEnvVar.java
... -XX:MaxHeapSize=9663676416 ...
❌ 无效! 仍使用默认值 9GiB。这并非 javac
特例——**除 java
命令外,所有 JDK 工具只识别 JAVA_TOOL_OPTIONS
**。
用 javadoc
验证:
$ JAVA_TOOL_OPTIONS="-Xmx10m" javadoc -J-XX:+PrintCommandLineFlags com/baeldung/TestEnvVar.java
Picked up JAVA_TOOL_OPTIONS: -Xmx10m
... -XX:MaxHeapSize=10485760 ...
$ JDK_JAVA_OPTIONS="-Xmx10m" javadoc -J-XX:+PrintCommandLineFlags com/baeldung/TestEnvVar.java | sed 's/ /\n/g' | grep MaxHeapSize
Loading source file com/baeldung/TestEnvVar.java...
... -XX:MaxHeapSize=9663676416 ...
结果一致:JAVA_TOOL_OPTIONS
生效,JDK_JAVA_OPTIONS
被忽略。
5. JDK_JAVA_OPTIONS vs. JAVA_TOOL_OPTIONS
总结核心差异:
特性 | JAVA_TOOL_OPTIONS | JDK_JAVA_OPTIONS |
---|---|---|
用途 | 为所有 JDK 工具传递 JVM 选项 | 仅给 Java 启动器传递选项 |
作用范围 | 所有 JDK 工具 (java/javac/javadoc等) | 仅 java 命令 |
支持版本 | Java 5+ | Java 9+ |
优先级 | 冲突时被 JDK_JAVA_OPTIONS 覆盖 |
更高优先级 |
使用建议:
- ✅ Java 9+ 启动应用 → 优先用
JDK_JAVA_OPTIONS
- ✅ 全局配置所有 JDK 工具 → 只能用
JAVA_TOOL_OPTIONS
- ⚠️ 避免同时设置冲突参数,优先级问题可能导致配置失效
6. 总结
通过实际测试,我们明确了两个关键差异:
- 作用域不同:
JAVA_TOOL_OPTIONS
影响所有 JDK 工具,JDK_JAVA_OPTIONS
仅作用于java
命令 - 优先级不同:在
java
命令中,JDK_JAVA_OPTIONS
的配置会覆盖JAVA_TOOL_OPTIONS
合理使用这两个变量,可以:
- 避免重复配置 JVM 参数
- 灵活控制不同工具的运行时行为
- 减少因手动修改脚本导致的踩坑风险
下次需要动态传递 JVM 选项时,根据场景选择合适的变量,配置管理将变得简单高效!