ByteScrollGet the app
☰ Topics
jdk-jre-jvm18 / 200‹›
JAVA / BASICS3 minute read

JDK vs JRE vs JVM — what's the difference?

Easy

The JVM runs bytecode: it loads classes, verifies them, interprets or JIT-compiles them, and collects garbage. The JRE is a JVM plus the standard libraries, enough to run apps. The JDK adds tools to build them: javac, jar, jshell, jlink and more.

How it works

  1. JVM (Java Virtual Machine). An abstract machine defined by a spec, with many implementations (HotSpot in OpenJDK, Eclipse OpenJ9, GraalVM).
    • Loads .class files and verifies the bytecode is safe.
    • Runs it with an interpreter at first, then JIT-compiles hot methods to native code.
    • Manages memory and garbage collection, threads, and the link to the OS.
    • The JVM itself is platform-specific; the bytecode it runs is not. That's "write once, run anywhere".
  2. JRE (Java Runtime Environment). A JVM plus the Java class libraries (java.base, java.sql, java.net.http...). It's what a machine needs to run Java.
  3. JDK (Java Development Kit). A full runtime plus developer tools:
    • javac (compiler), jar, javadoc, jshell (REPL)
    • jdb, jcmd, jstack, jfr for debugging and profiling
    • jlink and jpackage for building custom runtimes and installers
  4. Modern packaging. Since Java 11, Oracle no longer ships a separate JRE. Some distributions (Eclipse Temurin, for one) still publish JRE builds, but the recommended path is a JDK for development and, for production, a trimmed runtime built with jlink that holds only the modules your app needs.
JDKJREjavac (JDK)Order.javaOrder.class(bytecode)JVM: classloader,verifier,interpreter +JIT, GCjavac, jar,jshell, jlink,jcmd...ClasslibrariesNative code onLinux / macOS/ Windows
JDKJREjavac (JDK)Order.javaOrder.class(bytecode)JVM: classloader,verifier,interpreter +JIT, GCjavac, jar,jshell, jlink,jcmd...ClasslibrariesNative code onLinux / macOS/ Windows

Example

Example.javaJava
// Greeter.java
public class Greeter {
    public static void main(String[] args) {
        System.out.println("Running on " + System.getProperty("java.vm.name")
                + " " + Runtime.version());
    }
}
Example.javatext
$ javac Greeter.java          # JDK tool: produces Greeter.class
$ java Greeter                # JVM runs the bytecode
$ java Greeter.java           # Java 11+: compile in memory and run a single file
$ jlink --add-modules java.base --output myruntime   # tiny custom runtime

Edge cases

  • java Greeter.java (JEP 330, Java 11) launches a single source file without a separate javac step. Java 22 extended this to programs spread over several source files.
  • The same .class file runs on any JVM of the same or newer version. Running it on an older JVM fails with UnsupportedClassVersionError.
  • The JIT means a Java program usually gets faster after warm-up, which matters for benchmarks and short-lived CLI tools.

Common mistakes

  • Saying the JVM is platform-independent. The bytecode is; each OS and CPU needs its own JVM build.
  • Thinking you need a JDK on production servers "because Java". You need a runtime; a jlink image is smaller and has less attack surface.
  • Mixing up "Java version" for compile and run: javac --release 17 targets 17 even when built with a JDK 21.

Likely follow-up

"What happens between java Greeter and main running?" The JVM starts, the bootstrap and platform class loaders load core classes, the application class loader loads Greeter, the bytecode is verified, static initializers run, and then main is invoked on the main thread.

Get every deep dive in the app

Coming soon to the App StoreComing soon to Google Play