What state does a warm Gradle Daemon keep in memory between builds, and how does that affect build times?
answer
- JIT-compiled hot code stays warm
- parsed scripts + configured model in heap
- cached file/dependency metadata resident
- first build cold ≈ no-daemon
- daemon warmth ≠ build/configuration cache (disk)
basics
~20 sA warm daemon keeps JIT-compiled hot code and in-memory state — parsed build scripts, the configured project model, and cached file/dependency metadata. So later builds skip JVM startup and warmup and reuse that state, making them faster than the first build.
solid answer
~50 sBecause the daemon is a **resident JVM**, it preserves two kinds of warmth between builds: 1. **JVM/runtime warmth** — the **HotSpot JIT** has already profiled and compiled Gradle's hot code paths to native code, so a later build runs optimized rather than re-interpreting from cold. 2. **In-memory build state** — parsed and compiled build scripts, the configured project model, and cached file-system and dependency metadata stay resident, so Gradle doesn't have to re-read and re-parse everything from scratch. The visible effect is a warmup curve: the **first** build on a fresh daemon is about as slow as a no-daemon build, but **subsequent** builds get progressively faster as the JIT optimizes and caches fill — typically stabilizing after a few runs. This is distinct from the **build cache** and **configuration cache**, which persist task outputs / the configured graph *to disk* across machines and daemon restarts. The daemon's warmth is purely **in-memory and per-process** — restart the daemon and it's lost.
code
bash · 6 lines# Demonstrate the warmup curve: time successive builds on one daemon
./gradlew help # build 1: cold daemon, slowest
./gradlew help # build 2: warmer
./gradlew help # build 3+: steady-state, fastest
# Restart the daemon and warmth resets:
./gradlew --stop && ./gradlew help # cold againgo deeper
Know the daemon keeps things 'warm' so repeat builds are faster than the first.
Name the two layers — JIT warmup and in-memory build state — and that the first build is cold.
Clearly separate in-memory daemon warmth from disk-backed build/configuration caches and explain how they compose, including the restart-loses-warmth nuance.
Reason about steady-state vs cold-build performance for capacity planning, benchmarking methodology, and which optimizations (warm daemon vs caches) to invest in across dev and CI fleets.
## Two layers of 'warmth' A daemon's speedup comes from keeping state that a fresh JVM would have to rebuild. ### 1. JVM runtime warmth (JIT) HotSpot starts by **interpreting** bytecode, then profiles which methods are hot and **JIT-compiles** them to optimized native code. This warmup takes several runs of the hot paths. A persistent daemon keeps that compiled code, so build #5 runs already-optimized code that build #1 had to warm up from scratch. This is the single biggest reason later daemon builds feel snappier. ### 2. In-memory build state The daemon also holds, in its heap, structures that are expensive to recreate: - **Parsed / compiled build scripts** — your `build.gradle(.kts)` and `settings.gradle(.kts)` don't have to be re-read and re-compiled if unchanged. - **The configured project model** — the result of the configuration phase. - **Cached file-system and dependency metadata** — e.g. info about files and resolved dependency descriptors that doesn't have to be re-fetched/re-stat'd every time. ## The warmup curve ``` Build #1 (cold daemon): ████████████ (≈ no-daemon: full startup + warmup) Build #2: ███████ Build #3: █████ Build #4+: ████ (stabilized — warm) ``` This is why benchmarking Gradle by timing a single cold run is misleading: you measure warmup, not steady-state. To compare fairly, warm the daemon first, then measure several runs. ## How this differs from the caches (important!) Don't conflate daemon warmth with Gradle's persistent caches: | Mechanism | Where it lives | Survives daemon restart? | Survives across machines? | |---|---|---|---| | **Daemon warmth** (JIT + in-memory state) | Daemon JVM heap | **No** | No | | **Build cache** (task outputs) | Disk (local or remote) | Yes | Yes (remote) | | **Configuration cache** (configured task graph) | Disk (`.gradle/configuration-cache`) | Yes | No (machine-local) | Key takeaway: the daemon's warmth is **per-process and in-memory** — kill or restart the daemon and it's gone. The build cache and configuration cache are **on disk** and persist independently. They compose: a warm daemon *plus* a populated configuration cache *plus* a build cache is the fastest path, each removing a different cost. ## Practical implications - **First build is always 'slow'** — even with a daemon, because the JVM is cold. Don't expect the daemon to speed up a single from-scratch run. - **Memory matters** — keeping all that state resident uses heap; tune `org.gradle.jvmargs` if builds are large. - **Restarting the daemon resets warmth** — after a daemon stop (or an idle expiry, or a JVM-args change that forces a new daemon), you pay the warmup again.
- Does the daemon's in-memory warmth survive a daemon restart?No. JIT-compiled code and in-memory build state live in the daemon's JVM heap, so stopping or expiring the daemon discards them and the next build is cold again. Only disk-backed caches (build cache, configuration cache) survive.
- How is daemon warmth different from the configuration cache?Daemon warmth is in-memory and per-process — lost on restart. The configuration cache serializes the configured task graph to disk under .gradle/configuration-cache, so it persists across daemon restarts (but stays machine-local). They complement each other.
saying these in an interview costs you the question
- Claiming the daemon's warm state persists to disk or across machines — it's in-memory and per-process.
- Equating the daemon with the build cache or configuration cache; they are distinct, disk-backed mechanisms.
- Expecting a single cold build to show the daemon's speedup.