Java
Programming
Environment Variables
Java Options
Software Development

Difference between _JAVA_OPTIONS, JAVA_TOOL_OPTIONS and JAVA_OPTS

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

_JAVA_OPTIONS, JAVA_TOOL_OPTIONS, and JAVA_OPTS are three environment variables used to pass JVM arguments, but they work at different levels. JAVA_TOOL_OPTIONS is the only one defined by the official JVM specification (JVMTI) and is picked up by every compliant JVM. _JAVA_OPTIONS is a HotSpot-specific extension that silently overrides everything. JAVA_OPTS is not recognized by the JVM at all; it is a convention used by application server startup scripts like Tomcat's catalina.sh and JBoss's standalone.sh. Choosing the wrong one causes settings to be silently ignored or to affect processes you did not intend to change.

How Each Variable Works

JAVA_TOOL_OPTIONS

This is the officially supported variable, defined in the JVMTI (JVM Tool Interface) specification. Every compliant JVM is required to check this variable on startup and prepend its contents to the command-line arguments.

bash
export JAVA_TOOL_OPTIONS="-Xmx512m -Denv=staging"
java -jar myapp.jar
# JVM prints: Picked up JAVA_TOOL_OPTIONS: -Xmx512m -Denv=staging

Key characteristics:

  • The JVM prints a message to stderr confirming it picked up the variable. This is mandatory per the specification and cannot be suppressed.
  • Options from JAVA_TOOL_OPTIONS are prepended, so explicit command-line arguments override them.
  • It affects every Java process started in the environment, including tools like javac, jps, jstack, and build tools (Maven, Gradle) that spawn child JVMs.

_JAVA_OPTIONS

This is a HotSpot-specific (Oracle/OpenJDK) extension. It is not part of any specification and does not exist in all JVM implementations.

bash
export _JAVA_OPTIONS="-Xmx1024m -XX:+UseG1GC"
java -jar myapp.jar
# JVM prints: Picked up _JAVA_OPTIONS: -Xmx1024m -XX:+UseG1GC

Key characteristics:

  • Options from _JAVA_OPTIONS are appended to the command line, meaning they override explicit command-line arguments.
  • Like JAVA_TOOL_OPTIONS, it prints a stderr message confirming pickup.
  • Because it overrides explicit arguments, it can silently change behavior that was intentionally set on the command line.
  • It is HotSpot-specific. IBM J9, GraalVM native-image, and other non-HotSpot JVMs may not recognize it.

JAVA_OPTS

This variable is not recognized by the JVM. It exists purely as a convention in startup scripts.

bash
export JAVA_OPTS="-Xmx1024m -Xms512m"
./catalina.sh start    # Tomcat reads JAVA_OPTS in its script
java -jar myapp.jar    # This process ignores JAVA_OPTS entirely

Key characteristics:

  • Only works if the startup script explicitly reads it. Tomcat's catalina.sh, WildFly's standalone.sh, and some other scripts include $JAVA_OPTS in the java command they construct.
  • Has no effect when you run java directly.
  • Does not print any pickup message because the JVM never sees it.

Comparison Table

FeatureJAVA_TOOL_OPTIONS_JAVA_OPTIONSJAVA_OPTS
Defined byJVMTI specificationHotSpot implementationConvention (scripts)
Recognized by JVMYes (all compliant JVMs)Yes (HotSpot only)No
Argument positionPrepended (overridden by CLI args)Appended (overrides CLI args)Depends on script
Prints pickup messageYes (mandatory)YesNo
Affects javac, jps, etc.YesYesNo
Affects direct java -jarYesYesNo
Portable across JVMsYesNo (HotSpot only)N/A

Precedence and Override Behavior

When multiple variables are set, the effective argument order matters:

bash
export JAVA_TOOL_OPTIONS="-Xmx256m"
export _JAVA_OPTIONS="-Xmx1024m"
java -Xmx512m -jar myapp.jar

The effective command line becomes:

 
java -Xmx256m -Xmx512m -Xmx1024m -jar myapp.jar

For -Xmx (and most JVM flags), the last value wins. So the effective max heap is 1024m from _JAVA_OPTIONS, because it is appended last. This is why _JAVA_OPTIONS is considered dangerous: it silently overrides values you explicitly set on the command line.

 
1Priority (last wins for most flags):
21. JAVA_TOOL_OPTIONS  (prepended, lowest priority)
32. Command-line args  (middle)
43. _JAVA_OPTIONS      (appended, highest priority)

Practical Examples

Setting Heap Size for All Java Processes

bash
1# Recommended: use JAVA_TOOL_OPTIONS (portable, can be overridden)
2export JAVA_TOOL_OPTIONS="-Xmx512m"
3
4# All Java processes default to 512m, but can be overridden:
5java -Xmx1024m -jar myapp.jar  # uses 1024m (CLI overrides)

Forcing a Specific Heap Size (Cannot Be Overridden)

bash
1# Use _JAVA_OPTIONS to enforce a limit (HotSpot only)
2export _JAVA_OPTIONS="-Xmx512m"
3
4# Even explicit CLI args are overridden:
5java -Xmx2048m -jar myapp.jar  # uses 512m (_JAVA_OPTIONS wins)

Configuring Tomcat Specifically

bash
1# JAVA_OPTS only affects Tomcat (or whichever script reads it)
2export JAVA_OPTS="-Xmx2048m -Xms1024m -XX:+UseG1GC"
3./catalina.sh start
4
5# Other Java processes on the same machine are unaffected
6java -jar another-app.jar  # does not see JAVA_OPTS

Docker Container Configuration

In Docker containers, environment variables are the standard way to configure JVM options:

dockerfile
1# Dockerfile
2FROM eclipse-temurin:17-jre
3COPY myapp.jar /app/myapp.jar
4
5# Use JAVA_TOOL_OPTIONS for container-level defaults
6ENV JAVA_TOOL_OPTIONS="-Xmx512m -XX:+UseContainerSupport"
7
8ENTRYPOINT ["java", "-jar", "/app/myapp.jar"]
bash
# Override at runtime with docker run
docker run -e JAVA_TOOL_OPTIONS="-Xmx1024m" myapp

Using JAVA_TOOL_OPTIONS here is preferable because it is portable and can be overridden by the container orchestrator without modifying the Dockerfile.

Kubernetes JVM Configuration

yaml
1apiVersion: apps/v1
2kind: Deployment
3spec:
4  template:
5    spec:
6      containers:
7        - name: myapp
8          image: myapp:latest
9          env:
10            - name: JAVA_TOOL_OPTIONS
11              value: "-Xmx768m -XX:MaxRAMPercentage=75.0"
12          resources:
13            limits:
14              memory: "1Gi"

Debugging with Remote Attach

bash
1# Enable remote debugging for all local Java processes
2export JAVA_TOOL_OPTIONS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005"
3java -jar myapp.jar
4# Debugger can attach to port 5005

Be careful: this opens a debug port on every Java process, including mvn and gradle commands.

Which One Should You Use

ScenarioRecommended VariableReason
Default settings for all Java processesJAVA_TOOL_OPTIONSPortable, overridable, spec-compliant
Tomcat/WildFly/JBoss configurationJAVA_OPTSRead by the server's startup script
Force a setting that cannot be overridden_JAVA_OPTIONSAppended last, overrides CLI (use sparingly)
Docker/Kubernetes environmentJAVA_TOOL_OPTIONSClean, portable, works with all JVMs
Debugging a single applicationCLI argumentsNo side effects on other processes
CI/CD pipeline defaultsJAVA_TOOL_OPTIONSAffects build tools and tests uniformly

Common Pitfalls

  • Using JAVA_OPTS and expecting it to work with java -jar: JAVA_OPTS is not read by the JVM. If you set JAVA_OPTS="-Xmx1024m" and run java -jar myapp.jar, the heap is unchanged. Use JAVA_TOOL_OPTIONS instead.
  • Setting _JAVA_OPTIONS globally and forgetting about it: Because it overrides CLI arguments, it can silently change the behavior of build tools, IDEs, and test runners. A global _JAVA_OPTIONS="-Xmx256m" can cause out-of-memory errors in Maven builds that need more heap.
  • Noise from the pickup message: Both JAVA_TOOL_OPTIONS and _JAVA_OPTIONS print a message to stderr on every JVM startup. In scripts that parse stderr for errors, this message can trigger false alerts. The message cannot be suppressed.
  • Affecting unintended processes: Setting JAVA_TOOL_OPTIONS in a shell profile means every Java process in that shell inherits it, including javac, jps, keytool, Maven, Gradle, and IDE-launched processes. Scope the variable to specific commands when possible: JAVA_TOOL_OPTIONS="-Xmx1g" java -jar myapp.jar.
  • Confusing prepend vs. append semantics: JAVA_TOOL_OPTIONS is prepended (CLI wins), _JAVA_OPTIONS is appended (it wins). Getting this backwards leads to settings that are silently overridden.
  • Non-HotSpot JVMs ignoring _JAVA_OPTIONS: If you deploy on IBM Semeru, Azul Zing, or GraalVM native images, _JAVA_OPTIONS may be ignored entirely. Use JAVA_TOOL_OPTIONS for portability.

Summary

  • JAVA_TOOL_OPTIONS is the spec-compliant, portable choice. It is prepended to the command line, so explicit CLI arguments override it. Use this as your default.
  • _JAVA_OPTIONS is HotSpot-specific and appended to the command line, meaning it overrides explicit CLI arguments. Use it only when you need to force a setting that cannot be changed.
  • JAVA_OPTS is not a JVM variable. It is a convention used by application server startup scripts (Tomcat, WildFly). It has no effect on direct java invocations.
  • In Docker and Kubernetes, use JAVA_TOOL_OPTIONS as the environment variable for JVM configuration.
  • Scope JVM environment variables as narrowly as possible to avoid unintentionally affecting build tools, IDEs, and other Java processes.

Course illustration
Course illustration

All Rights Reserved.