skip to content

You are deciding between classLoaderIsolation and processIsolation for a worker-based task. What concrete factors push you from one to the other?

level: seniorimportance: should knowfreq 35%

answer

  1. both expose classpath — axis is blast radius
  2. version clash only → classLoaderIsolation
  3. heap/JVM args/System.exit/native crash → processIsolation
  4. leaky threads/static state → fork
  5. fork cost softened by worker-daemon reuse

basics

~20 s

Use classLoaderIsolation when you only need a separate classpath (version isolation) and the work is well-behaved. Move to processIsolation when work needs its own heap/JVM args, may call System.exit, may crash natively, or must not touch daemon memory.

solid answer

~50 s

Both give you a custom `classpath`, so the deciding question is: **do you need process-level isolation or just class isolation?** Stay with `classLoaderIsolation` (cheaper — no fork, runs in the daemon) when the only problem is a library-version clash and the action is trustworthy: no `System.exit`, no native code that can segfault, no need for distinct heap/JVM flags, and acceptable memory pressure inside the daemon. Escalate to `processIsolation` when: the work is **memory-hungry** and deserves its own heap; it needs **specific JVM args** (GC, encoding, agents); it might **call `System.exit` or crash natively**, which in-daemon would kill the build JVM; it leaks threads/static state you can't trust in the daemon; or it must be hermetically sandboxed. The cost is a JVM fork, partially recovered by Gradle's worker-daemon reuse. Net: classLoaderIsolation for *classpath* problems, processIsolation for *process/memory/crash-safety* problems.

go deeper

for a junior

State the simple rule: separate classpath only → classLoaderIsolation; needs its own JVM → processIsolation.

for a middle

List concrete triggers for forking: heap, JVM args, System.exit, native crashes.

for a senior

Reason about blast radius and cost: both share the classpath capability; the fork buys crash/memory containment at a price softened by daemon reuse.

for a principal

Define org guidance for when plugins must fork (e.g. any tool that can exit/crash) versus default to classloader isolation for performance.

## Same classpath power, different blast radius A frequent misconception is that the choice is about whether you can set a custom classpath — both `classLoaderIsolation` and `processIsolation` let you do that. The real axis is **what failure modes you need to contain**. ## Stay with classLoaderIsolation when… - The only issue is a **dependency version conflict** with Gradle's own classpath — a separate classloader fixes it. - The action is **well-behaved**: it returns normally, doesn't call `System.exit`, doesn't load native libraries that could crash the JVM, and doesn't leak threads. - The work's **memory footprint is modest** and acceptable inside the daemon JVM. - You want lower latency — no fork, just classloader setup. ## Escalate to processIsolation when… - **Heap pressure**: the work needs a large or independently tuned heap. Inside the daemon, a big allocation competes with Gradle and other in-daemon workers; a fork gives it a private `maxHeapSize`. - **JVM arguments**: you need a particular GC, a `-javaagent`, a different `file.encoding`, assertions, etc., set via `forkOptions.jvmArgs` / `systemProperty`. - **System.exit or native crashes**: any code that can terminate the JVM (some legacy tools, JNI/native libs) would take the daemon down with it if run in-process. A fork confines the damage to a disposable worker. - **Untrusted/leaky code**: static-state mutation, dangling threads, thread-locals — a fresh process discards all of it. - **Hard sandboxing**: separate environment variables, working directory, or charset. ## The cost and how Gradle softens it Forking a JVM is the most expensive mode, but Gradle maintains a pool of worker daemons and **reuses** idle ones whose fork options + classpath match the next submission. Keeping fork options stable maximizes reuse; varying them per-submission spawns extra JVMs. ## A decision checklist ```text Need a separate classpath? both can do it Need a separate heap / JVM args? → processIsolation Might call System.exit / crash native? → processIsolation Leaky threads / static state? → processIsolation Just a version clash, trustworthy? → classLoaderIsolation No isolation need at all? → noIsolation ``` ## Worked example A linter that bundles an old ASM version but is otherwise clean → `classLoaderIsolation`. A document renderer that shells into a native library and occasionally segfaults, needing 3 GB → `processIsolation` with `maxHeapSize = "3g"`.

  • A tool calls System.exit(1) on failure. Why must it not run under noIsolation or classLoaderIsolation?
    Both run in the Gradle daemon JVM, so System.exit would terminate the daemon itself, killing the whole build. processIsolation confines the exit to the disposable forked worker.
  • Does processIsolation guarantee the work cannot affect the build at all?
    It isolates memory, classloader, and the process, so in-JVM corruption can't reach the daemon. But shared filesystem, network, and the parameters/results you pass are still common ground — isolation is of the JVM, not of the world.

classLoaderIsolation is giving a contractor their own toolbox in your house; processIsolation is sending them to a separate building — if they set off the sprinklers, your house stays dry.

saying these in an interview costs you the question

  • Choosing processIsolation purely 'to be safe' for trivial work, paying fork cost needlessly.
  • Believing classLoaderIsolation protects against System.exit or native crashes — it does not.

context