skip to content

What does the Kotlin compiler's jvmTarget option control, and what happens if you target a higher bytecode version than the JDK that actually runs your app?

level: juniorimportance: must knowfreq 70%

answer

  1. jvmTarget = bytecode class-file version
  2. Newer target on older JVM = UnsupportedClassVersionError
  3. JVM is backward compatible, not forward
  4. jvmToolchain pins JDK + aligns target
  5. major version: 52=8, 61=17, 65=21

basics

~10 s

jvmTarget sets which Java bytecode version the compiler produces, like 17 or 21. If you produce bytecode newer than the JVM running it, the app fails to start with an UnsupportedClassVersionError.

solid answer

~40 s

compilerOptions.jvmTarget (a JvmTarget enum value like "17", "21") tells kotlinc which Java bytecode class-file version to emit. It does NOT choose which JDK compiles the code — only the format of the .class files. At runtime the JVM checks each class file's major version; if a class was compiled for a newer target than the running JVM supports, you get a java.lang.UnsupportedClassVersionError at class load. Picking jvmTarget also gates which JVM features the backend can lower to. The safe modern way to keep target and toolchain consistent is jvmToolchain(n), which provisions a JDK of that version and sets both Kotlin's jvmTarget and Java's sourceCompatibility/targetCompatibility together, removing the chance of a mismatch.

code

kotlin · 9 lines
kotlin
kotlin {
    // Preferred: one knob pins the JDK and aligns jvmTarget + Java compatibility
    jvmToolchain(21)

    // Or set bytecode target explicitly (must be <= deployment JVM)
    compilerOptions {
        jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
    }
}

go deeper

for a junior

Knows jvmTarget sets bytecode version and that too-new bytecode won't run on an older JVM.

for a middle

Distinguishes jvmTarget from jvmToolchain, names UnsupportedClassVersionError, knows backward-but-not-forward compatibility.

for a senior

Explains how toolchain aligns Kotlin + Java compatibility and prevents mixed-module mismatches; reasons about deployment JVM constraints.

for a principal

Sets org-wide toolchain policy, reasons about reproducible builds and class-file version 65/61 mapping across many services.

## What jvmTarget is `jvmTarget` is a Kotlin compiler option that selects the **Java bytecode (class-file) version** the compiler writes into every `.class` file it produces. Values are a fixed enum: `"1.8"`, `"9"` … `"21"`, `"24"`, etc. (the `JvmTarget` type in the Gradle DSL). It answers one question only: *what format should the output `.class` files be in?* It does **not** decide which JDK is used to run the compiler. ```kotlin // Gradle Kotlin DSL (Kotlin 2.x, modern compilerOptions block) kotlin { compilerOptions { jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_21) } } ``` ## Why the version matters at runtime Every `.class` file starts with a **major version** number in its header (e.g. 52 = Java 8, 61 = Java 17, 65 = Java 21). When the JVM loads a class, it refuses any class whose major version is **higher** than the JVM itself supports, throwing `java.lang.UnsupportedClassVersionError`. So bytecode compiled for target 21 simply will not load on a Java 17 JVM. Going the other way is fine: a Java 21 JVM happily runs target-17 bytecode (the JVM is backward compatible). ## jvmTarget vs jvmToolchain - **jvmTarget** = format of emitted bytecode. - **jvmToolchain(n)** = which JDK Gradle downloads/uses to compile *and* run. Setting `jvmToolchain(21)` makes Gradle provision a JDK 21 and **automatically** aligns Kotlin's `jvmTarget` and Java's `sourceCompatibility`/`targetCompatibility` to 21. Using the toolchain is the recommended approach because it removes the classic footgun of compiling with a new JDK but forgetting to set `jvmTarget`, or vice versa. ```kotlin kotlin { jvmToolchain(21) // pins JDK + aligns jvmTarget } ``` ## Common mistakes - Setting `jvmTarget` higher than your deployment JVM → `UnsupportedClassVersionError`. - Mismatching Kotlin's `jvmTarget` and Java's `targetCompatibility` in a mixed Kotlin+Java module (Gradle can warn/fail). The toolchain prevents this.

  • If you set jvmToolchain(21), do you still need to set jvmTarget manually?
    No. jvmToolchain(21) automatically sets Kotlin's jvmTarget and Java's source/targetCompatibility to 21, so an explicit jvmTarget is redundant (and would only matter if you wanted a lower bytecode than the JDK).
  • Can a Java 17 JVM run code compiled with jvmTarget 11?
    Yes. The JVM is backward compatible, so it runs any bytecode targeting its own version or lower.

jvmTarget is like writing a document in a new file format — an old reader app can't open a format newer than it knows.

saying these in an interview costs you the question

  • Thinking jvmTarget chooses which JDK compiles the code
  • Believing a newer-target class can run on an older JVM
  • Confusing jvmTarget with the Kotlin languageVersion
  • Not knowing the error is UnsupportedClassVersionError
  • Claiming JVMs are forward compatible

context