skip to content

Explain the difference between the 'Gradle JVM' and the 'build JDK' when a toolchain is declared. Can a build run Gradle on JDK 17 but compile with JDK 21?

level: middleimportance: must knowfreq 60%

answer

  1. Gradle JVM = runs the daemon
  2. build JDK = toolchain compiles code
  3. JAVA_HOME / org.gradle.java.home vs java { toolchain }
  4. can differ: run 17, compile 21
  5. ./gradlew -q javaToolchains

basics

~20 s

The Gradle JVM is the JDK that launches Gradle; the build JDK is the toolchain JDK that compiles and runs your code. They can differ — yes, Gradle can run on JDK 17 while a declared toolchain compiles with JDK 21.

solid answer

~50 s

There are two distinct JDKs in any Gradle build. The **Gradle JVM** is the JDK that starts the Gradle daemon (resolved from `JAVA_HOME`/wrapper/`org.gradle.java.home`); it must satisfy Gradle's own minimum-JDK requirement. The **build JDK** is the toolchain you declare: ```kotlin java { toolchain { languageVersion = JavaLanguageVersion.of(21) } } ``` Gradle uses the build JDK only for `JavaCompile`, `Test`, and `JavaExec`. These two can be different: you can launch Gradle on JDK 17 and still compile/test your product on JDK 21, because Gradle locates a separate JDK 21 installation for the toolchain. This is the whole point — it decouples the version your tooling needs from the version your code targets. If the toolchain JDK isn't found locally and provisioning is disabled, the build fails with a message naming the missing version, rather than silently compiling with the Gradle JVM.

code

bash · 2 lines
bash
# List detected JDKs and which the toolchain resolves to
./gradlew -q javaToolchains

go deeper

for a junior

Recall that two JDKs exist: one runs Gradle, one (the toolchain) compiles your code, and they can differ.

for a middle

Explain how each is resolved, that they decouple cleanly, and give the run-17/compile-21 example plus the javaToolchains command.

for a senior

Discuss the upgrade-Gradle-independently-of-target-JDK benefit and the fail-fast guarantee when the toolchain JDK is absent.

for a principal

Tie it to platform strategy: pinning Gradle JVM via org.gradle.java.home org-wide while letting teams choose target toolchains for reproducible CI.

## Two JVMs, one build Every Gradle build involves at least two Java runtimes, and conflating them is the most common toolchain misunderstanding. ### 1. The Gradle JVM This is the JDK that *runs Gradle itself* — the daemon process executing your build logic. It is resolved (in priority order) from: - `org.gradle.java.home` in `gradle.properties` (explicit override), - the `JAVA_HOME` environment variable, - the JDK on `PATH`. The Gradle JVM must meet Gradle's **minimum supported JDK** (e.g. modern Gradle requires a fairly recent JDK to run). Plugins that run inside the build (annotation processors loaded into the build, custom plugin code) execute on this JVM. ### 2. The build JDK (the toolchain) This is the JDK Gradle uses to **compile, test, and run your application code**. It's what you declare: ```kotlin java { toolchain { languageVersion = JavaLanguageVersion.of(21) } } ``` Gradle wires this toolchain into `JavaCompile` (via its `javaCompiler`), `Test` (via `javaLauncher`), and `JavaExec`/`application` (via `javaLauncher`). ## Can they differ? Yes — that's the feature A classic scenario: a build plugin or the Gradle version you're on requires JDK 17 to *run*, but your product must be compiled and tested on JDK 21. With toolchains: - Gradle daemon launches on JDK 17 (the Gradle JVM). - The `java` toolchain points at a separately installed JDK 21. - All your compile/test/run tasks use JDK 21. Gradle locates the JDK 21 installation through its detection mechanisms; if absent and provisioning is on, it can download one. The reverse also holds: run Gradle on JDK 21 while a legacy module is pinned to a JDK 11 toolchain. ## Why decoupling matters - **You can upgrade Gradle independently of your target JDK.** Bumping Gradle (which may require a newer Gradle JVM) doesn't force your bytecode target to change. - **Reproducibility**: the compile JDK is explicit in the build script, not an accident of the environment. - **CI parity**: CI runs the same toolchain version as developers, even if the agent's default JDK differs. ## How to inspect it Run `./gradlew -q javaToolchains` to print every JDK Gradle detected and which one the toolchain resolves to — invaluable when a build picks an unexpected JDK. `./gradlew buildEnvironment` and the build scan also surface the Gradle JVM. ## Failure behaviour If the requested toolchain version isn't available locally and auto-provisioning is disabled (or no resolver is configured), Gradle fails with an error naming the exact version it needed — it does **not** silently fall back to the Gradle JVM. That fail-fast behaviour is what guarantees the build can't quietly compile against the wrong JDK.

  • Where does the Gradle JVM get resolved from?
    In priority: org.gradle.java.home in gradle.properties, then JAVA_HOME, then the JDK on PATH. It must meet Gradle's minimum supported JDK.
  • What happens if the declared toolchain JDK isn't installed and provisioning is off?
    The build fails fast with an error naming the required version. Gradle does not silently fall back to compiling with the Gradle JVM.
  • Which command shows what Gradle detected?
    ./gradlew -q javaToolchains lists all detected JDK installations and the one each toolchain request resolves to.

saying these in an interview costs you the question

  • Saying Gradle 'just uses JAVA_HOME to compile' when a toolchain is declared.
  • Assuming the toolchain version and the Gradle JVM version must match.
  • Believing Gradle silently falls back to the Gradle JVM if the toolchain JDK is missing.

context