00:00

JDK vs JRE vs JVM – Differences Explained with Examples

When you start with Java you quickly meet three abbreviations: JDK, JRE and JVM. They are nested parts of the same platform. The JVM runs Java programs, the JRE is the JVM plus the libraries a program needs, and the JDK is the JRE plus the tools to write and build programs.

This tutorial explains each one, how a program travels from source code to running code, and what has changed in modern Java versions.

1. JDK, JRE and JVM in One Picture

JDK contains JRE, which contains the JVM
The nesting
JDK  = JRE + development tools (javac, jar, javadoc, jdb, jshell, jlink ...)
JRE  = JVM + class libraries (java.lang, java.util, java.io ...) + supporting files
JVM  = the engine that loads, verifies and runs bytecode

Think of a car factory: the JVM is the engine, the JRE is the complete car you can drive, and the JDK is the whole workshop with the tools to build cars.

2. What Is the JVM?

The Java Virtual Machine is a program that executes Java bytecode. It is called "virtual" because it behaves like a computer with its own instruction set, but it is implemented in software on top of the real operating system.

The JVM is responsible for:

  • Loading .class files when they are first needed.
  • Verifying bytecode so it cannot break type or memory rules.
  • Executing bytecode with an interpreter and a JIT compiler.
  • Managing memory, including garbage collection.
  • Providing threads, security checks and exception handling.

The JVM itself is platform-specific: there is a different JVM build for Windows, macOS and Linux on each processor type. That is what allows the same bytecode to run everywhere. The most widely used JVM is HotSpot, part of OpenJDK.

3. What Does Bytecode Look Like?

Here is a tiny program and the bytecode javac produces for it, shown with the JDK's javap -c disassembler:

Calculator.java
public class Calculator {
    static int add(int a, int b) {
        return a + b;
    }

    public static void main(String[] args) {
        System.out.println("2 + 3 = " + add(2, 3));
    }
}
Output
2 + 3 = 5
javap -c Calculator (excerpt)
static int add(int, int);
  Code:
     0: iload_0       // push a
     1: iload_1       // push b
     2: iadd          // add them
     3: ireturn       // return the result

public static void main(java.lang.String[]);
  Code:
     0: getstatic     #7    // Field java/lang/System.out
     3: iconst_2
     4: iconst_3
     5: invokestatic  #13   // Method add:(II)I
     8: invokedynamic #19   // build the string "2 + 3 = ..."
    13: invokevirtual #23   // Method PrintStream.println
    16: return

Bytecode is a simple stack-based language: values are pushed onto a stack, and instructions such as iadd work on the top of the stack. The JVM turns these instructions into real machine code for the processor it is running on.

4. Inside the JVM

1. Class loader subsystem

Classes are loaded on demand through a hierarchy of class loaders, then linked (verified, prepared and resolved) and initialized (static fields set and static blocks run).

ClassLoaderDemo.java
public class ClassLoaderDemo {
    public static void main(String[] args) {
        // Bootstrap loader (shown as null): core classes such as String
        System.out.println("String          -> " + String.class.getClassLoader());

        // Platform loader: other Java SE modules such as java.sql
        System.out.println("java.sql.Date   -> " + java.sql.Date.class.getClassLoader().getName());

        // Application loader: your own classes from the class path
        System.out.println("ClassLoaderDemo -> " + ClassLoaderDemo.class.getClassLoader().getName());
    }
}
Output
String          -> null
java.sql.Date   -> platform
ClassLoaderDemo -> app
  • Bootstrap loader (shown as null): loads core classes such as java.lang.String.
  • Platform loader: loads other Java SE modules.
  • Application loader: loads your classes from the class path or module path.

2. Runtime data areas

AreaShared?Holds
HeapAll threadsEvery object and array; managed by the garbage collector.
Metaspace (method area)All threadsClass metadata, method code, the constant pool.
Java stackOne per threadA frame per method call with local variables and partial results.
PC registerOne per threadThe address of the current bytecode instruction.
Native method stackOne per threadFrames for native (C/C++) methods.

3. Execution engine

  • Interpreter: runs bytecode instruction by instruction, so the program starts quickly.
  • JIT compiler: compiles frequently executed methods into optimised native code (HotSpot uses the C1 and C2 compilers).
  • Garbage collector: reclaims memory from unreachable objects (G1 is the default; ZGC and others are available).

5. What Is the JRE?

The Java Runtime Environment is everything needed to run a Java program: the JVM plus the Java class libraries (java.lang, java.util, java.io, java.net and so on) and supporting configuration files. It does not include the compiler.

Modern Java: since Java 11, vendors no longer ship a separate JRE download. You install a JDK, and if you need a small runtime for deployment, you build one containing only the modules your app uses with jlink.

6. What Is the JDK?

The Java Development Kit is what developers install. It contains the runtime plus the tools to write, build, debug and package Java programs:

ToolPurpose
javacCompiles .java source files into .class bytecode.
javaLaunches the JVM to run a program.
jarPackages classes and resources into a JAR file.
javadocGenerates HTML documentation from comments.
jshellInteractive console for trying Java code (Java 9+).
javapShows the contents and bytecode of class files.
jdbCommand-line debugger.
jlinkBuilds a custom, minimal runtime image.
jpackageCreates native installers (.exe, .dmg, .deb).
jcmd, jstack, jfrDiagnose and profile running JVMs.

Popular JDK distributions, all built from OpenJDK, include Eclipse Temurin, Oracle JDK, Amazon Corretto, Microsoft Build of OpenJDK, Azul Zulu and GraalVM.

7. From Source Code to Running Program

  1. You write Calculator.java.
  2. JDK: javac Calculator.java creates Calculator.class.
  3. JRE / JVM: java Calculator starts the JVM.
  4. The class loader loads Calculator and the library classes it uses.
  5. The verifier checks the bytecode.
  6. The interpreter starts running main; hot code is JIT-compiled.
  7. The garbage collector cleans up unused objects while the program runs.
Terminal
javac Calculator.java          # JDK tool: compile
java Calculator                # JVM: run
javap -c Calculator            # JDK tool: show bytecode
java -version                  # which JDK/JVM is installed

8. JDK vs JRE vs JVM: Comparison

AspectJDKJREJVM
Full formJava Development KitJava Runtime EnvironmentJava Virtual Machine
PurposeDevelop and run Java programsRun Java programsExecute bytecode
ContainsJRE + development toolsJVM + class librariesClass loader, memory areas, execution engine
Includes compiler (javac)YesNoNo (contains the JIT compiler only)
Who needs itDevelopersUsers running Java appsPart of both
Platform-specificYesYesYes

Instead of installing a full JDK on a server, you can create a trimmed runtime that contains only the modules your application uses:

Terminal
# Which modules does my app need?
jdeps --print-module-deps app.jar

# Build a runtime with just those modules
jlink --add-modules java.base,java.sql --output my-runtime --strip-debug --no-header-files --no-man-pages

# Run the app with the custom runtime
my-runtime/bin/java -jar app.jar

This is how modern container images keep Java applications small.

10. Summary

  • The JVM loads, verifies and runs bytecode, manages memory and JIT-compiles hot code.
  • The JRE is the JVM plus the class libraries needed to run programs.
  • The JDK is the JRE plus development tools such as javac, jar, jshell and jlink.
  • All three are platform-specific; the bytecode they run is not.
  • Since Java 11, you install a JDK and use jlink for small runtimes.

Related Tutorials

Reference: The Java Virtual Machine Specification, Java SE 21