环境变量 JDK_JAVA_OPTIONS 与 JAVA_TOOL_OPTIONS 之间有什么区别?

1、概览

在配置 Java 应用时,我们经常需要在不修改脚本的情况下传递 JVM 选项。

我们可以使用环境变量 JDK_JAVA_OPTIONSJAVA_TOOL_OPTIONS,而不是每次运行 java 命令时都手动添加标记。这两个环境变量的作用相同:动态传递 JVM 选项,但它们的工作方式不同。

本文将带你了解它们的区别、何时使用每种配置以及有效管理 JVM 配置的最佳实践。

2、JDK_JAVA_OPTIONS 和 JAVA_TOOL_OPTIONS 是什么?

这两个环境变量都允许我们在全局范围内指定 JVM 选项,从而省去了每次执行 JDK 工具(如 javajavacjavadoc 等)时修改选项的麻烦。

JAVA_TOOL_OPTIONS 在 Java 5 中引入。它们的行为和目的截然不同。

在深入了解每个环境变量的功能之前,先创建一个简单的 Java 源码文件:

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") + "'");
    }

}

上面的代码非常简单。我们的 main() 方法会打印出 var1var2 的值。稍后,我们将使用这两个环境变量把参数传递给这个类。

接下来,编译代码:

$ javac com/baeldung/TestEnvVar.java

现在,文件结构如下

$ tree
.
└── com
    └── baeldung
        ├── TestEnvVar.class
        └── TestEnvVar.java

3 directories, 1 file

顺便说一下,我们将在本教程中使用 Java 23Linux 上编译或运行所有示例:

$ 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 命令。

首先,在 JDK_JAVA_OPTIONS 中定义 var1var2,然后启动 main() 方法:

$ 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 变量,并将 var1var2 的值传递给了 main() 方法,产生了预期的结果。此外,java 还输出了一条信息,告知我们 JDK_JAVA_OPTIONS 已被读取。

需要注意的是,我们在同一行设置了环境变量并执行了 java 命令,中间用空格隔开。这就确保了 JDK_JAVA_OPTIONS 只为特定的 java 命令设置。如果我们在运行命令后检查该变量,就会发现 JDK_JAVA_OPTIONS 不再有值:

$ echo $JDK_JAVA_OPTIONS

现在,让我们在 JAVA_TOOL_OPTIONS 中设置 var1var2,然后运行类:

$ 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 变量也会影响 Java Launcher(启动器)。此外,类似的信息还告诉我们 java 命令读取了 JAVA_TOOL_OPTIONS

现在,有人可能会问:如果在 JDK_JAVA_OPTIONSJAVA_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)'

在本例中,我们在 JAVA_TOOL_OPTIONS 中定义了 var1var2,但只在 JDK_JAVA_OPTIONS 中用不同的值定义了 var2。输出结果显示,java 获取了这两个环境变量。但如果在 JDK_JAVA_OPTIONSJAVA_TOOL_OPTIONS 中设置了相同的变量,JDK_JAVA_OPTIONS 优先

4、其他 Java 命令

除了 java 命令外,JDK 还提供了其他一些命令,使我们能够高效地编译、记录和管理 Java 应用,例如

  • javac - Java 编译器
  • javadoc - JavaDoc 生成器
  • 等…

接下来,让我们弄清楚这些工具如何处理 JDK_JAVA_OPTIONSJAVA_TOOL_OPTIONS

我们以 javac 编译器为例。与 java 命令不同,通过 javac 检查变量值并不直接。因此,为了测试其行为,我们将在 JDK_JAVA_OPTIONSJAVA_TOOL_OPTIONS 中设置最大堆大小 (-Xmx) JVM 参数,并验证它是否会如预期般影响 javac

首先,检查一下 javac 默认的 MaxHeapSize 值:

$ javac -J-XX:+PrintCommandLineFlags com/baeldung/TestEnvVar.java
... -XX:MaxHeapSize=9663676416 ...

在本例中,-J-XX:+PrintCommandLineFlags 选项用于某些 JDK 工具(本例中为 javac),以在工具启动时打印出 JVM 正在使用的命令行标志。

如输出所示,在这台机器上,JVM 默认将 9GiB (9102410241024=9663676416 字节) 内存作为 MaxHeapSize。接下来,让我们在 JAVA_TOOL_OPTIONS 中设置 10MiB(1010241024 = 10485760 字节)为 MaxHeapSize,然后再次编译源文件:

$ JAVA_TOOL_OPTIONS="-Xmx10m" javac -J-XX:+PrintCommandLineFlags com/baeldung/TestEnvVar.java
Picked up JAVA_TOOL_OPTIONS: -Xmx10m
... -XX:MaxHeapSize=10485760 ...

如输出所示,JAVA_TOOL_OPTIONS 会影响 javac。

接下来,对 JDK_JAVA_OPTIONS 变量进行同样的测试:

$ JDK_JAVA_OPTIONS="-Xmx10m" javac -J-XX:+PrintCommandLineFlags com/baeldung/TestEnvVar.java
... -XX:MaxHeapSize=9663676416 ...

原来 JDK_JAVA_OPTIONS 并不影响 javac。它仍然使用默认选项编译源文件: MaxHeapSize=9GiB.

实际上,这不仅仅是 javac 的问题。它适用于除 java 命令之外的所有 JDK 工具 - 只有 JAVA_TOOL_OPTIONS 才能被识别。

接下来,使用 JDK 的另一个工具 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 ...

可以看到,该命令忽略了 JDK_JAVA_OPTIONS 中设置的 -Xmx10m 参数,但正确识别了 JAVA_TOOL_OPTIONS 变量中的值。

5、JDK_JAVA_OPTIONS 和 JAVA_TOOL_OPTIONS

了解了这两个环境变量如何与 JDK 工具配合使用后来总结一下它们的区别:

对比面 JAVA_TOOL_OPTIONS JDK_JAVA_OPTIONS
目的 用于将 JVM 选项和参数传递给 JDK 工具的环境变量 环境变量,用于将 JVM 选项和参数传递给 Java Launcher 以启动 Java 应用
范围 它会影响所有 JDK 工具(javajavacjavadocjar 等)。 它只影响 java 命令
版本限制 Java 5+ Java 9+
Java Launcher 的优先级 JDK_JAVA_OPTIONS 中的值会覆盖此变量中设置的相同选项 优先

由于 JDK_JAVA_OPTIONS 环境变量是在 Java 9 中引入的,并且是 java 命令的专属变量,因此在 Java 9 或更高版本上设置启动应用程序的选项时应使用该变量。但是,如果我们需要在 JDK 工具中全局设置选项,则应选择 JAVA_TOOL_OPTIONS

6、总结

本文通过实际示例介绍了环境变量 JDK_JAVA_OPTIONSJAVA_TOOL_OPTIONS 之间的区别,以及应该在何时使用哪个变量。


Ref:https://www.baeldung.com/java-jdk_java_options-vs-java_tool_options