skip to content

Why does JMeter, not the JVM, handle a -D flag typed on the jmeter command line?

level: middleimportance: should knowfreq 41%

answer

  1. Look at the launcher script's last line
  2. Your flags land after -jar
  3. JMeter's own parser, not the java launcher
  4. Applied during initializeProperties, not at boot

basics

~10 s

The bin/jmeter launcher places everything you type after -jar ApacheJMeter.jar, so the JVM treats it as an application argument. JMeter's own parser matches -D and calls System.setProperty during its startup.

solid answer

~40 s

`bin/jmeter` ends with `java $ARGS $JVM_ARGS $JMETER_OPTS -jar "$PRGDIR/ApacheJMeter.jar" "$@"`. Your arguments arrive in `"$@"`, which is *after* `-jar`, and a java launcher stops reading options once it has identified the artefact to run — so the JVM never inspects them. They are handed to JMeter's `main`, whose parser matches `-D` and, inside `JMeter.initializeProperties`, calls `System.getProperties().setProperty(name, value)`. Two things follow. A genuine launcher option — heap sizing, a GC choice, a java agent — cannot be supplied this way, because those are only read before `-jar`. And the property is set once JMeter's startup reaches that point, so anything sampled during JVM boot or in an earlier static initialiser will not see it. Behaviour described is Apache JMeter 6.0.0.

go deeper

for a junior

Know that the jmeter command is a shell script wrapping a java command, and that what you type on it is passed to JMeter rather than to the JVM. That is enough to stop you reaching for -Xmx there.

for a middle

Explain the launcher line: arguments after -jar are application arguments, so JMeter's own parser handles -D and calls System.setProperty during startup. Name the consequence for a genuine launcher option.

for a senior

Diagnose the timing case: a value read by a static initialiser that runs before JMeter's property phase will not see the flag. Check jmeter.log for the Setting System property line before blaming the setting itself.

for a principal

Decide where the boundary sits for a team — which settings are per-run flags on the jmeter command line and which are properties of how an injector is built and launched, so the two are never confused in a job definition.

A `-D` typed on JMeter's command line looks exactly like a JVM launcher option, and that resemblance is the trap. The JVM never sees it. JMeter parses it, and JMeter applies it, some milliseconds into its own startup. ## Where your arguments actually go The last line of `bin/jmeter` builds the java command like this: ```sh "$JAVA_HOME/bin/java" $ARGS $JVM_ARGS $JMETER_OPTS -jar "$PRGDIR/ApacheJMeter.jar" "$@" ``` Everything you typed arrives in `"$@"`, which sits **after** `-jar`. A java launcher stops consuming options at the point it identifies the main artefact; from there on, every word is an application argument handed to `main(String[])`. So `jmeter -n -t plan.jmx -Dfoo=bar` gives the JVM no `-D` at all — it gives JMeter a four-element argument array. ## What JMeter does with it `JMeter.start` builds a `CLArgsParser` over that array. `-D` matches the descriptor registered as `systemproperty`, and `JMeter.initializeProperties` executes: ```java case SYSTEM_PROPERTY -> { if (!value.isEmpty()) { log.info("Setting System property: {}={}", name, value); System.getProperties().setProperty(name, value); } else { log.warn("Removing System property: {}", name); System.getProperties().remove(name); } } ``` So the property is genuinely set — through the ordinary `System.setProperty` API, from inside the running application. ## What follows from the timing Two consequences matter in practice: - **A real launcher option cannot be supplied this way.** Heap sizing, a garbage collector choice, a `-javaagent`, a module flag: those are consumed only before `-jar`, so typing them on the jmeter command line either produces an unknown-option error from JMeter's own parser or is silently taken as something else. - **Anything that reads the property earlier will miss it.** `initializeProperties` runs early — it is the first thing `JMeter.start` does inside its try block — but "early in JMeter" is still after the JVM has booted, after the launcher jar's classes have initialised and after log4j2 has been configured. A static initialiser that samples a system property before that point sees the old value. For most settings this is irrelevant, because the code that consumes the property reads it when it needs it. `RmiUtils`, for example, calls `System.getProperties().getProperty("java.rmi.server.hostname")` at the moment it resolves a host, long after the property phase has run. The timing only bites where a value is sampled once, in a class initialiser that ran earlier. ## How to tell which happened `jmeter.log` settles it. If JMeter applied the flag, there is a line for it: ``` INFO o.a.j.JMeter: Setting System property: javax.net.ssl.trustStore=/etc/pki/prod.jks ``` If the line is absent, JMeter never processed that flag — check that it was actually accepted and not swallowed as an argument to a preceding option. And if you needed the value before JMeter started at all, it does not belong on the jmeter command line; it belongs on the java command that the launcher script assembles, ahead of `-jar`. ## The staging-to-production angle This is why a plan that serves staging and production keeps the two kinds of difference apart. The endpoint, the user count and any plan-visible switch travel as `-J`, applied by JMeter and readable from the plan. A per-environment trust store travels as `-D`, applied by JMeter but read by JSSE. Anything that has to shape the JVM itself is not a per-run flag at all — it is part of how the injector is launched. Behaviour described is Apache JMeter 6.0.0.

  • Does that mean a -D flag on the jmeter command line is unreliable?
    Not for the usual cases. Anything that reads its system property at the point of use is fine: RmiUtils, for instance, looks up java.rmi.server.hostname when it resolves a host, well after JMeter's property phase. It is unreliable only for a value sampled once in a class initialiser that runs before JMeter.start reaches initializeProperties.
  • How would you set a genuine JVM option for a JMeter run instead?
    It has to reach the java command ahead of -jar, which is what the launcher script's own argument variables are for. Nothing you can type after the jmeter command will get there, because every one of those words is an application argument by the time the JVM has parsed the command.

saying these in an interview costs you the question

  • Says the java launcher parses -D from the jmeter command line
  • Claims heap size can be set with a jmeter -D flag
  • Thinks the property is applied before the JVM starts
  • Assumes JMeter forwards unrecognised flags to the JVM