Why might Gradle start a brand-new daemon instead of reusing the existing one, and how do JVM args cause this?
answer
- JVM args fixed at launch
- different -Xmx / JDK / -D = incompatible
- new daemon spawned, sprawl
- gradle --status shows many idle
- fix: pin args in gradle.properties
basics
~20 sA daemon can only run builds with the JVM args and Java version it was started with. If your request needs different org.gradle.jvmargs, JDK, or system properties, Gradle can't reuse it — it spawns a new, incompatible daemon.
solid answer
~50 sDaemon reuse is conditional: the client will only attach to an **idle daemon whose JVM configuration matches** the build's requirements. The JVM args a daemon runs with are fixed at startup — you can't change a live JVM's heap, garbage collector, or system properties. So if your request demands a different `org.gradle.jvmargs` (e.g. a bigger `-Xmx`), a different Java home/toolchain, or different daemon JVM system properties, no existing daemon qualifies and Gradle starts a **new** one. Common triggers: an env var or `JAVA_OPTS` that varies between invocations, mixing `gradle.properties` settings across projects, IDE vs CLI passing different args, or scripts that set `-D` flags inconsistently. The symptom is daemon sprawl — running `gradle --status` shows several daemons, many idle, and reuse never sticks. The fix is to make JVM args deterministic and identical across every invocation: pin them in the project's `gradle.properties` and stop injecting per-run overrides.
code
properties · 3 lines# gradle.properties — pin once so every run lands on the SAME daemon
org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8
# Avoid: setting JVM args via JAVA_OPTS or per-run -D that differs between invocationsgo deeper
Know that a daemon runs with fixed JVM settings and different settings mean a new daemon.
Name the concrete triggers (org.gradle.jvmargs, JDK, daemon -D props) and the sprawl symptom via --status.
Diagnose reuse failures across IDE/CLI/CI, and prescribe a deterministic gradle.properties strategy.
Standardize JVM config across the org/monorepo so a single warm daemon serves all projects; treat sprawl as a measurable productivity cost.
## Reuse is conditional, not automatic When the client looks for a daemon to attach to, it doesn't grab just any running one. It needs an **idle, compatible** daemon. Compatibility is determined largely by the **JVM the daemon was started with** — because a running JVM's startup parameters are immutable. ## Why JVM args force a new daemon Many JVM options can only be set at process launch: maximum heap (`-Xmx`), the garbage collector, file encoding, and `-D` system properties baked into the daemon. A live JVM cannot be reconfigured to a different `-Xmx` mid-flight. Therefore, if the incoming build request specifies JVM args that differ from what a candidate daemon was launched with, that daemon is **incompatible** and is skipped. If none match, Gradle starts a fresh daemon with the requested args. The relevant setting is: ```properties # gradle.properties org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8 ``` Change any of these between runs and you fragment your daemon pool. ## What also breaks compatibility - **Different Java version / JAVA_HOME** — a daemon runs on one JDK; requesting another spawns a new daemon on that JDK. - **Daemon JVM system properties** — `-D` properties that affect the daemon JVM (not just `-P` project properties). - **Inconsistent environment** — `JAVA_OPTS`, IDE-injected args, or per-script `-D` flags that differ from the CLI. ## The symptom: daemon sprawl Run: ```bash gradle --status ``` and you'll see multiple daemons, several `IDLE`, each with slightly different JVM args. Reuse never sticks, every build starts cold-ish (a new daemon), and you waste memory holding redundant JVMs. ## The fix — determinism Make the JVM configuration **identical for every invocation**: 1. Define `org.gradle.jvmargs` once in the project `gradle.properties` and commit it. 2. Avoid setting JVM args via env vars or ad-hoc `-D` flags that vary. 3. Align IDE and CLI so they request the same args and JDK. 4. If sprawl already happened, `gradle --stop` to clear old daemons, then run with the canonical config so one warm daemon serves everything. The principle: a daemon is only as reusable as your JVM configuration is consistent.
- If two projects on disk use different org.gradle.jvmargs, what happens to daemon reuse when you switch between them?Each project's request needs a differently-configured JVM, so Gradle keeps (at least) two daemons — one per arg set — and you don't get cross-project reuse.
- Does passing a -P project property force a new daemon?No. -P project properties are part of the build request, not the daemon JVM launch config, so they don't fragment daemons. Daemon-JVM -D system properties and org.gradle.jvmargs do.
- How would you confirm sprawl is happening?Run `gradle --status` and look for multiple daemons with differing JVM args; many will be IDLE because nothing reuses them.
saying these in an interview costs you the question
- Saying you can change a daemon's heap at runtime to make it compatible — JVM launch args are immutable.
- Confusing -P project properties (don't fragment daemons) with daemon JVM args (do).
- Claiming Gradle reuses any idle daemon regardless of JVM config.