A teammate reports that Gradle keeps starting new daemons and builds feel slow, even though a daemon should already be running. What would you investigate?
answer
- new daemon every build = nothing compatible
- JAVA_HOME differs (IDE vs terminal vs CI)
- conflicting org.gradle.jvmargs / per-command args
- mixed Gradle versions -> separate registries
- fix: standardize JDK + jvmargs + wrapper
basics
~10 sLikely each build requests a different JVM or JVM args, so no existing daemon is compatible. Check that JAVA_HOME and org.gradle.jvmargs are consistent across every invocation.
solid answer
~50 sConstant fresh daemons mean **no idle daemon is compatible**, so the client keeps spawning new ones. The usual culprits: **`JAVA_HOME` differs between shells/IDE/CI**, so each invocation needs a daemon on a different JDK; **`org.gradle.jvmargs` differs** between project and user `gradle.properties`, or someone passes JVM args **on the command line** per build (which can even force single-use daemons); or builds run under **different Gradle versions** (each has its own registry). I'd compare the resolved Java home and JVM args across invocations, inspect `~/.gradle/daemon/<version>/` for many entries with different parameters, and check for an idle daemon that simply never matches. The fix is to **standardize** the JDK (ideally pin via `org.gradle.java.home` or a consistent toolchain launcher) and JVM args so every build converges on one reusable daemon. Memory pressure can also cause Gradle to expire daemons aggressively, masking as 'no reuse'.
code
bash · 4 lines# Diagnose identity drift
./gradlew --version # shows Gradle version + JVM the daemon will use
echo "$JAVA_HOME" # compare to org.gradle.java.home and the IDE's JDK
ls -lt ~/.gradle/daemon/8.7/ # many fresh daemons => proliferationgo deeper
Suspect different Java versions or JVM args between runs; check JAVA_HOME is consistent.
Systematically check JDK, jvmargs, and Gradle version as identity factors and inspect the daemon registry for proliferation.
Distinguish identity-mismatch spawns from memory-pressure expiry; prescribe pinning JDK/jvmargs and using the wrapper everywhere.
Establish org-wide toolchain/JVM-arg standards and CI agent configs so identity drift is structurally prevented.
## Symptom: 'Starting a Gradle Daemon' every time If reuse worked, you'd see that line once and then silence on later builds. Seeing it repeatedly means **every invocation fails the compatibility test** against the daemons already registered, so a new one is forked each time. This both wastes startup time (cold JIT) and piles up daemons that hold heap (**daemon proliferation**). ## Investigate the identity factors Compatibility hinges on JDK + JVM args + version. Walk each: - **JAVA_HOME / resolved JDK** — the IDE may launch Gradle on one JDK while the terminal uses another; `org.gradle.java.home` may differ from the shell's `JAVA_HOME`. Each distinct Java home needs its own daemon. This is the most frequent cause. - **org.gradle.jvmargs** — a project `gradle.properties` and the user-level `~/.gradle/gradle.properties` may set conflicting args; or CI passes different `-Xmx`. Mismatched args => incompatible. - **Command-line JVM overrides** — passing JVM args per invocation can force **single-use** daemons that never persist, so you never see reuse at all. - **Gradle version** — wrapper vs. system Gradle, or mixed versions across modules, split daemons across per-version registries. ## How to confirm ```bash # Compare what each environment actually resolves ./gradlew --version # prints the JVM and Gradle version in use echo "$JAVA_HOME" # shell's JDK ls ~/.gradle/daemon/ # one dir per version ``` Look in the per-version daemon directory for many recently created daemons, and check whether an idle one exists whose parameters never match your request. ## The fix Converge identities: - Pin the JDK the daemon runs on (consistent `JAVA_HOME` / `org.gradle.java.home`, or a uniform toolchain launcher) across terminal, IDE, and CI. - Put all stable JVM args in **one** authoritative `gradle.properties`; stop passing them per command. - Use the **wrapper** everywhere so the Gradle version is identical. After that, almost every invocation finds the same idle daemon and reuses it. Note too that under memory pressure Gradle may **expire** idle daemons, which can also look like 'no reuse' — but the dominant cause of *new spawns every build* is identity mismatch.
- Why would the IDE and the terminal end up using different daemons?They may launch Gradle on different JDKs (different JAVA_HOME / toolchain), so each needs a daemon with a matching Java home; the daemons are not interchangeable.
- Besides identity mismatch, what else can look like 'no reuse'?Under memory pressure Gradle may expire idle daemons early, so a daemon you expected to reuse is gone; this manifests similarly but stems from resource limits, not incompatibility.
- What single change usually fixes most proliferation?Standardizing the JDK (consistent JAVA_HOME / org.gradle.java.home) across terminal, IDE, and CI so all invocations share one daemon.
saying these in an interview costs you the question
- Blaming the build script logic rather than JVM/identity drift.
- Recommending more heap when the real issue is each build requesting a different identity.
- Ignoring per-command JVM-arg overrides that force single-use forks.