skip to content

When using processIsolation, how do you configure the forked worker JVM — for example its heap size and JVM arguments — and what API exposes those settings?

level: middleimportance: should knowfreq 40%

answer

  1. ProcessWorkerSpec → classpath + forkOptions
  2. forkOptions is a JavaForkOptions
  3. maxHeapSize / jvmArgs / systemProperty / environment
  4. independent of daemon org.gradle.jvmargs
  5. worker daemons reused when fork options match

basics

~10 s

Call workerExecutor.processIsolation { forkOptions { ... } }. forkOptions is a JavaForkOptions where you set maxHeapSize, minHeapSize, jvmArgs, systemProperty, environment, and the working directory of the forked JVM.

solid answer

~40 s

`processIsolation(Action<ProcessWorkerSpec>)` gives you a `ProcessWorkerSpec`, which exposes two things: a `classpath` (a `ConfigurableFileCollection`) and `forkOptions`, a `JavaForkOptions`. Through `forkOptions` you tune the forked worker JVM exactly like any Java process: `maxHeapSize = "2g"`, `minHeapSize`, `jvmArgs("-XX:+UseG1GC")`, `systemProperty("k", "v")`, `environment(...)`, `bootstrapClasspath`, `defaultCharacterEncoding`, and `workingDir`. You can call `forkOptions { ... }` (a configuration block) or `forkOptions(Action)`. This is the whole reason to reach for processIsolation when work is memory-hungry or needs special JVM flags: each worker gets its own, independently tuned heap instead of competing inside the Gradle daemon. Gradle keeps forked worker JVMs warm and reuses them across builds when their fork configuration matches, so the per-fork cost is amortized.

code

groovy · 8 lines
groovy
workerExecutor.processIsolation { spec ->
    spec.classpath.from(configurations.tool)
    spec.forkOptions { fork ->
        fork.maxHeapSize = '2g'
        fork.jvmArgs '-XX:+HeapDumpOnOutOfMemoryError'
        fork.systemProperty 'mode', 'ci'
    }
}

go deeper

for a junior

Know that processIsolation lets you set maxHeapSize and JVM args via forkOptions.

for a middle

Name the JavaForkOptions surface (heap, jvmArgs, systemProperty, environment) and that it lives only on ProcessWorkerSpec.

for a senior

Explain worker-daemon reuse, why fork options should stay stable, and that the worker heap is independent of the daemon heap.

for a principal

Set conventions for right-sizing worker heaps in shared plugins to avoid memory blow-ups under parallel execution at org scale.

## Where fork settings live Only **processIsolation** forks a JVM, so only it lets you configure that JVM. The factory `WorkerExecutor.processIsolation(Action<ProcessWorkerSpec>)` hands you a `ProcessWorkerSpec` with: - `getClasspath(): ConfigurableFileCollection` — the worker's classpath - `forkOptions` — a `JavaForkOptions` describing how to launch the JVM `JavaForkOptions` is the same abstraction used by `JavaExec` and test forking, so the knobs are familiar. ## The knobs that matter - **Heap**: `maxHeapSize` / `minHeapSize` (e.g. `"512m"`, `"2g"`). This is the headline reason to fork — give heavy work its own heap rather than starving or bloating the daemon. - **JVM args**: `jvmArgs("-XX:+UseG1GC", "-XX:+HeapDumpOnOutOfMemoryError")` or `setJvmArgs(list)`. - **System properties**: `systemProperty("file.encoding", "UTF-8")` or `systemProperties(map)`. - **Environment**: `environment("VAR", "value")`. - **Other**: `bootstrapClasspath`, `defaultCharacterEncoding`, `workingDir`, `executable` (a specific java launcher), `enableAssertions`. ## Worker-daemon reuse Forking sounds expensive, but Gradle pools worker daemons. After a fork finishes, the JVM stays idle and can be **reused** for the next compatible submission — "compatible" meaning the same fork options and classpath. This is why you should keep fork options stable across submissions: divergent options spawn new worker JVMs and defeat reuse. ## Picking a heap The forked worker heap is independent of `org.gradle.jvmargs` (which sizes the daemon itself). Size the worker for the task's actual footprint; oversizing wastes memory across parallel workers, undersizing risks OOM in the fork (which, thanks to isolation, won't crash the daemon). ```kotlin workerExecutor.processIsolation { classpath.from(configurations["analyzer"]) forkOptions { minHeapSize = "256m" maxHeapSize = "1500m" jvmArgs("-XX:+UseParallelGC") systemProperty("analyzer.mode", "strict") defaultCharacterEncoding = "UTF-8" } } ``` ## Common confusion `forkOptions` exists **only** on `ProcessWorkerSpec`. `noIsolation()` and `classLoaderIsolation()` run in the daemon JVM, so there is no per-work JVM to tune — trying to set a heap there is a category error.

  • Can you set forkOptions on a classLoaderIsolation queue?
    No. classLoaderIsolation runs in the Gradle daemon JVM and exposes only a classpath via ClassLoaderWorkerSpec — there is no forked JVM to configure, so forkOptions does not exist there.
  • Is the worker heap affected by org.gradle.jvmargs?
    No. org.gradle.jvmargs sizes the Gradle daemon. The forked worker's heap is set independently through forkOptions.maxHeapSize, so the two are decoupled.
  • Why keep fork options consistent across submissions?
    Gradle reuses idle worker daemons only when the fork options and classpath match. Divergent options force new JVMs and lose the reuse benefit.

saying these in an interview costs you the question

  • Thinking forkOptions is available in every isolation mode.
  • Confusing the worker heap with the Gradle daemon heap (org.gradle.jvmargs).

context