others-how to solve UnsupportedClassVersionError when running a kotlin application?

1. Purpose

In this post, I will demonstrate how to solve the following error when running a kotlin application:

> Task :bootRun FAILED
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.UnsupportedClassVersionError: com/example/blog/BlogApplicationKt has been compiled by a more recent version of the Java Runtime (class file version 61.0), this version of the Java Runtime only recognizes class file versions up to 52.0
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:756)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
Error: A JNI error has occurred, please check your installation and try again

    at java.net.URLClassLoader.defineClass(URLClassLoader.java:473)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
    at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:601)

Execution failed for task ':bootRun'.
> Process 'command '/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/bin/java'' finished with non-zero exit value 1

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

The core error message is :

Exception in thread "main" java.lang.UnsupportedClassVersionError: com/example/blog/BlogApplicationKt has been compiled by a more recent version of the Java Runtime (class file version 61.0), this version of the Java Runtime only recognizes class file versions up to 52.0

So the question is how to tell gradle(kotlin DSL) to change the JVM that compiles and runs the kotlin code.



2. Solution

The Enviroment of this problem is:

  • The bytecode is compiled by jdk17,which generate the class version 61
  • But the java version is jdk8 in the host, it can not recognized the new class version

So the problem is that the JAVA running the application does NOT match the class version that compiled the applcation.

To solve this problem, we have two solutions: 1) Compile the code with JDK8 and run it with JDK8+ 2) Or we can Compile it with JDK 17 and run it with JDK 17

To use the latest JDK features , I decide to use JDK11 to compile and run the application:

Change your gradle/wrapper/gradle-wrapper.properties to use the latest gradle version:

distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip

Because gradle team introduced a new feature JVM toolchain in 6.7 release.

Fortunately, the Gradle team introduced a new feature called JVM toolchain in the 6.7 release. Initially, only Java compilation was supported, but with the recent Gradle 7.2 release, Groovy and Scala compilations also work with JVM toolchains. The Kotlin Gradle plugin added support for this feature starting from the 1.5.30 release!

Then Change the build.gradle.kts to compile and run the kotlin code using JVM 11:

...
plugins {
    kotlin("jvm") version "1.7.20" apply false
}


tasks.withType<KotlinCompile> {
    kotlinOptions {
        freeCompilerArgs = listOf("-Xjsr305=strict")
        jvmTarget = "11"
    }
}

kotlin {
    jvmToolchain(11)
}

As you can see, we must use the gradle jvm plugin version later than 1.5.30, here we used 1.7.20.

For kotlinOptions/jvmTarget settings, here is the detail link, the key point is :

jvmTarget Target version of the generated JVM bytecode: “1.8”, “9”, “10”, …, “18” The default value for jvmTarget is 1.8.

For jvmToolchain:

In Kotlin, toolchain support affects only the Kotlin/JVM -jdk-home option’s value and additionally sets the -jvm-target value if it was not set explicitly by the user. Additionally, the toolchain’s major JDK version is considered as a task input now.

You can get more information from this link.



3. Summary

In this post, I demonstrated how to change kotlin application’s JVM version using the build.gradle.kts, if relies on the jvmToolChain feature provided by the gradle 6.7+. That’s it, thanks for your reading.