skip to content

Why might Gradle start a brand-new daemon and leave the old one idle even though one was already warm? How does idletimeout relate to that stale daemon?

level: seniorimportance: should knowfreq 35%

answer

  1. daemon JVM args/JDK fixed at fork
  2. incompatible → new daemon, old left idle
  3. stale daemon reclaimed at its idletimeout
  4. jvmargs/JDK change = common trigger
  5. --status to see IDLE/BUSY, --stop to clear

basics

~20 s

A daemon's JVM args and JDK are fixed at startup. If a build requests incompatible args (different heap, JDK, etc.), Gradle can't reuse the warm daemon, so it starts a fresh one. The old daemon sits idle and is only reclaimed when its idletimeout expires.

solid answer

~50 s

A daemon is launched with a fixed JVM: a specific JDK and a specific set of `org.gradle.jvmargs`. Those can't change at runtime. When a new build's required JVM characteristics don't **match** an existing daemon — say you bumped `-Xmx`, changed metaspace, or switched JDK — Gradle considers the warm daemon **incompatible**, leaves it running, and forks a brand-new compatible daemon. The original isn't force-killed; it simply becomes idle, and its `org.gradle.daemon.idletimeout` clock now governs when it exits. That's why people see two (or more) daemons after tweaking jvmargs: one stale + one new. Practical consequences: (1) churning jvmargs in dev multiplies idle daemons, so a sensible idletimeout matters; (2) running `gradle --stop` clears all of them immediately when you want a clean slate. I'd diagnose this with `gradle --status`, which shows each daemon and whether it's idle or busy.

code

bash · 6 lines
bash
$ gradle --status
   PID STATUS   INFO
 12345 IDLE     8.7 (incompatible: -Xmx2g)
 12346 BUSY     8.7
# old 12345 was orphaned after -Xmx changed; it exits at its idletimeout
$ gradle --stop   # or terminate all of them now

go deeper

for a junior

Know that changing JVM args forces a new daemon and the old one lingers idle.

for a middle

Explain JVM-arg/JDK immutability and that idletimeout governs the stale daemon's exit.

for a senior

Connect mismatch eviction to daemon multiplication, choosing a short idletimeout, and diagnosing with --status/--stop.

for a principal

Set org-wide jvmargs/JDK and idletimeout baselines so daemon mismatch churn and orphan memory are minimized across all environments.

## Daemons are immutable JVMs When Gradle forks a daemon, it bakes in: - the **JDK** (Java home) it runs on, and - the **JVM arguments** from `org.gradle.jvmargs` (heap, metaspace, GC flags, file encoding, etc.). These cannot be reconfigured on a live JVM. So the daemon's identity is essentially *(JDK, JVM args)*. ## Compatibility matching For each build, Gradle computes the JVM requirements and looks for an **idle, compatible** daemon. "Compatible" means the requested JDK and JVM args match the daemon's. If none match: - Gradle **does not** repurpose a warm-but-incompatible daemon (it can't). - It **forks a new** daemon with the right configuration. - The mismatched daemon keeps running as **idle**. Common triggers for a mismatch: - Changing `org.gradle.jvmargs` (e.g. `-Xmx2g` → `-Xmx4g`). - Switching the JDK / Java home used to run Gradle. - A different file encoding or other JVM flag in a project's `gradle.properties`. ## Where idletimeout comes in The stale daemon isn't killed — it's reclaimed only when **its own `idletimeout`** elapses. So: - With the **3-hour default**, a stale daemon you'll never reuse sits on memory for up to three hours. - A **shorter idletimeout** evicts these orphans quickly, which is exactly why teams that frequently change jvmargs or JDKs lower it. ```properties # A team that often switches heap settings keeps this short org.gradle.daemon.idletimeout=300000 # 5 min — stale daemons clear fast ``` ## Inspecting and clearing ```bash gradle --status # lists daemons: PID, Gradle version, status (IDLE / BUSY) gradle --stop # immediately terminate ALL daemons for this Gradle version ``` If you see several IDLE daemons after fiddling with JVM args, that's the eviction/mismatch behavior. `--stop` is the manual hammer; a tuned idletimeout is the automatic broom. ## Mental model Daemons are pre-configured machines on a factory floor. You can't re-tool a running machine, so a job needing different settings gets a *new* machine; the old one idles until its shutdown timer (idletimeout) powers it down — or you flip the master switch (`--stop`).

  • How do you immediately reclaim a stale incompatible daemon instead of waiting for its idletimeout?
    Run `gradle --stop`, which terminates all daemons for that Gradle version at once, giving you a clean slate without waiting for the idle timer.
  • Why can't Gradle just re-use the warm daemon and apply the new JVM args to it?
    JVM arguments (heap, metaspace, JDK, encoding) are fixed at process startup and can't be changed on a running JVM, so a new process is the only option for incompatible requirements.

Pre-tooled factory machines: a job needing different settings gets a new machine; the old one idles until its timer powers it down or you hit the master switch.

saying these in an interview costs you the question

  • Claiming Gradle reconfigures a live daemon's heap/JDK on the fly.
  • Saying the stale daemon is killed instantly — it idles until its own idletimeout (or --stop).
  • Confusing this with the daemon process limit rather than args incompatibility.

context