skip to content

How do you pass custom JVM and -XX flags to the test JVM in Gradle, and what's the difference between assigning and appending them?

level: middleimportance: must knowfreq 50%

answer

  1. jvmArgs(...) appends, jvmArgs = ... replaces
  2. Test implements JavaForkOptions
  3. -XX tuning, -ea, -javaagent per worker
  4. don't mix maxHeapSize with -Xmx jvmArg
  5. each fork gets the same args

basics

~10 s

On the test task use jvmArgs(...) to add flags like -XX:+HeapDumpOnOutOfMemoryError. Calling jvmArgs("-X...") appends; assigning jvmArgs = listOf(...) replaces the whole list. Heap shortcuts use minHeapSize/maxHeapSize.

solid answer

~40 s

The `test` task exposes `jvmArgs` for arbitrary flags passed to each forked test worker — `-XX` tuning flags, GC selection, `-ea`, agent attachment, etc. There are two forms: the **method** `jvmArgs("-XX:+UseG1GC", "-ea")` *appends* to the existing list, while the **property assignment** `jvmArgs = listOf(...)` *replaces* it wholesale (and would wipe anything Gradle or plugins added). Prefer the append form unless you deliberately want a clean slate. Heap-specific flags have idiomatic shortcuts (`maxHeapSize`/`minHeapSize`) — using those instead of raw `-Xmx`/`-Xms` keeps configuration readable and avoids duplicate conflicting flags. All of this is per forked worker, so each parallel fork receives the same args.

code

kotlin · 9 lines
kotlin
tasks.test {
    maxHeapSize = "2g"                      // prefer this over -Xmx jvmArg
    jvmArgs(                                // append form
        "-XX:+UseG1GC",
        "-XX:+HeapDumpOnOutOfMemoryError",
        "-ea",
    )
    // jvmArgs = listOf("-ea")  // BEWARE: this would replace everything above
}

go deeper

for a junior

Know jvmArgs(...) adds flags to the test JVM and heap has its own shortcuts.

for a middle

Crisply distinguish the appending method call from the replacing property assignment; avoid duplicate heap flags.

for a senior

Anticipate per-worker collisions (heap dump paths) and plugin-added flags being wiped by assignment; verify the launched command line.

for a principal

Codify a standard test-JVM flag set in a convention plugin; forbid blind reassignment to keep org-wide diagnostics flags intact.

## jvmArgs on the Test task The `Test` task implements `JavaForkOptions`, which gives it a mutable `jvmArgs` list applied to **every** forked test worker. You use it for anything you'd otherwise type on a `java` command line: - GC / tuning: `-XX:+UseG1GC`, `-XX:MaxMetaspaceSize=512m` - Diagnostics: `-XX:+HeapDumpOnOutOfMemoryError`, `-XX:HeapDumpPath=...` - Assertions: `-ea` (enable assertions) - Agents: `-javaagent:/path/agent.jar`, `-XX:+EnableDynamicAgentLoading` - Preview features: `--enable-preview` ## Append vs replace — a real footgun ```kotlin tasks.test { jvmArgs("-XX:+HeapDumpOnOutOfMemoryError") // APPENDS jvmArgs = listOf("-ea") // REPLACES the whole list } ``` The **method call** `jvmArgs(...)` adds to whatever is already there. The **assignment** `jvmArgs = ...` throws the existing list away. If a plugin or earlier configuration added important flags, an assignment silently drops them. As a rule, append unless you intend a reset. ## Don't duplicate heap flags Gradle gives `minHeapSize`/`maxHeapSize` as dedicated properties. Mixing `maxHeapSize = "2g"` with `jvmArgs("-Xmx1g")` produces two `-Xmx` flags; the JVM honors the last one, which is fragile and confusing. Pick one mechanism for heap. ## Inspecting what was actually passed Because workers are separate processes, add `-XX:+PrintFlagsFinal` or run with `--info` to see the launched command line when a flag seems ignored. ## Per-worker, not shared Every flag in `jvmArgs` is applied to each forked worker independently. With multiple forks, a flag like a fixed `-XX:HeapDumpPath` to a single file can collide — parameterize paths per worker if needed.

  • You wrote `jvmArgs = listOf("-ea")` and a profiler agent flag added by a plugin vanished. Why?
    Assigning to jvmArgs replaces the entire list, discarding flags added earlier. Use the append form `jvmArgs("-ea")` so the plugin's `-javaagent` survives.
  • A flag you added seems ignored. How do you confirm what the worker actually launched with?
    Run with --info to see the worker command line, or add -XX:+PrintFlagsFinal; remember each fork is a separate process so check the right one.

saying these in an interview costs you the question

  • Thinking `jvmArgs = listOf(...)` appends — it replaces.
  • Setting both maxHeapSize and a raw -Xmx jvmArg.
  • Assuming jvmArgs on test affects the daemon or compilation.

context