JDK, JRE, and JVM

Clarify the distinction between JDK, JRE, and JVM — the three pillars of Java development and runtime environments.

published: reading time: 18 min read author: Geek Workbench

JDK, JRE, and JVM

New Java developers often confuse JDK, JRE, and JVM. These three components form a layered architecture — each building on the previous. Understanding their boundaries helps you install the right package, debug runtime issues, and explain Java’s architecture in interviews.

When to Use

ComponentUse When
JDKYou are writing, compiling, or building Java code
JREYou are only running Java applications (no compilation)
JVMYou need to understand execution internals or tune runtime behavior

The Three Layers

Think of Java as a three-story building:

flowchart TB
    subgraph JDK["JDK (Java Development Kit)"]
        direction TB
        COMP[javac Compiler]
        LIBS[Tools + Libraries]
        JRE_L[JRE Included]
    end
    subgraph JRE["JRE (Java Runtime Environment)"]
        direction TB
        JVM_R[JVM]
        RT[JDK Library Classes]
    end
    subgraph JVM["JVM (Java Virtual Machine)"]
        direction TB
        EXEC[Execution Engine]
        MEM[Memory Management]
    end

    JDK --> JRE --> JVM
  • JVM — The engine that executes bytecode
  • JRE — JVM plus the class library needed to run Java applications
  • JDK — JRE plus development tools (compiler, debugger, profilers)

JDK (Java Development Kit)

The JDK is a full-featured development environment. It includes everything needed to develop, compile, and debug Java applications.

What’s Inside the JDK

ToolPurpose
javacJava compiler — transforms .java source files to .class bytecode
javaLauncher — starts a JVM and runs your application
javapDisassembler — inspects compiled .class files
javadocDocumentation generator — builds HTML API docs from annotations
jarArchiver — packages .class files and resources into .jar files
jdbDebugger — command-line debugging interface
jdepsDependency analyzer — lists package-level dependencies
jhsdbHotSpot debugger — inspects running JVM internals

JDK Editions

EditionTarget Platform
JDK SE (Standard Edition)Desktop, server, general-purpose applications
JDK EE (Enterprise Edition)Deprecated — Jakarta EE is the successor
JDK ME (Micro Edition)Mobile devices, embedded systems, IoT
OpenJDKOpen-source reference implementation (LGPL licensed)

JRE (Java Runtime Environment)

The JRE provides the runtime environment for end users who only need to run Java applications — not develop them. It includes the JVM and the core class library (java.lang, java.util, java.io, etc.).

What You Get with JRE

  • JVM — The bytecode executor
  • Core class library — rt.jar containing java.and javax. packages
  • Native libraries — Platform-specific .dll / .so files for native operations
  • Configuration files — java.policy for security permissions

JRE vs No-JRE

If you try to run java MyApp.class without a JRE installed, you get:

$ java MyApp
bash: java: command not found

Installing just the JRE (smaller download) lets users run applications but not compile them.

JVM (Java Virtual Machine)

The JVM is the abstract specification that defines how bytecode becomes machine instructions. Different vendors implement the JVM (HotSpot, OpenJ9, GraalVM, Azul Zing).

JVM Implementation Variants

ImplementationVendorKey Feature
HotSpotOracle, Eclipse TemurinTiered compilation, mature GC algorithms
OpenJ9EclipseFast startup, low memory footprint
GraalVMOracleNative image compilation, polyglot (JS, Python, Ruby)
Azul ZingAzulReadyNow! for predictable latency, pauseless GC

The JVM Specification vs Implementation

The Java Language Specification defines the language; the JVM Specification defines the runtime. The specification covers:

  • Class file format (bytecode instructions)
  • Runtime memory model
  • Thread synchronization primitives
  • Instruction set architecture (abstract, not x86-specific)

Implementations vary in performance characteristics, GC algorithms, and JIT optimization strategies — but all produce identical behavior for the same bytecode.

Production Failure Scenarios + Mitigations

Wrong JVM Installed for Your JDK Version

Scenario: Installing a JDK 17 but running an older JRE 11 that is incompatible with your application.

Symptoms:

  • UnsupportedClassVersionError at runtime
  • Application crashes on missing methods that exist in newer JDK

Mitigation:

# Check installed Java version
java -version
javac -version

# Set JAVA_HOME to correct JDK path
export JAVA_HOME=/usr/lib/jvm/temurin-17-jdk
export PATH=$JAVA_HOME/bin:$PATH

# In Maven/Gradle projects, enforce JVM version
# maven-compiler-plugin:
#   <source>17</source>
#   <target>17</target>

JRE Missing Core Library Classes

Scenario: Application deployed to minimal JRE image missing EE libraries (e.g., JAXB, JAX-WS).

Mitigation:

# Verify all dependencies are on classpath
java -cp "lib/*:app.jar" com.example.Main

# Use jdeps to check dependency graph
jdeps --print-deps myapp.jar

# Package full JDK for deployment if dependencies are uncertain
# Or use jlink to create custom runtime image with required modules
jlink --add-modules java.base,java.logging,com.example.mymodule --output myruntime

JVM Vendor Lock-In

Scenario: Application assumes HotSpot-specific flags that fail on OpenJ9.

Mitigation:

# Test with target JVM before deployment
# Avoid vendor-specific flags like:
#   -XX:+UseStringDeduplication    # HotSpot only
#   -XX:+UseJVMCICompiler          # HotSpot only

# Stick to standard flags documented across all JVMs:
#   -Xmx, -Xms, -XX:+UseG1GC, -XX:MaxGCPauseMillis=200

Trade-off Table

DecisionJDK AdvantageJDK Disadvantage
Full JDK vs JRE onlyCan compile, debug, and diagnose locallyLarger disk and memory footprint
HotSpot vs OpenJ9Most tested, widest GC optionsHigher memory usage
OpenJDK vs Oracle JDKFree, open-source, no license concernsOracle JDK includes additional commercial tools
GraalVM native imageFast startup, small container imagesLong build times, limited reflection support

Implementation Snippet: Verifying Your Java Environment

import java.util.Properties;

public class JavaEnvironmentCheck {
    public static void main(String[] args) {
        Properties props = System.getProperties();

        System.out.println("=== Java Environment ===");
        System.out.printf("Java Version:    %s%n", props.get("java.version"));
        System.out.printf("JVM Vendor:      %s%n", props.get("java.vendor"));
        System.out.printf("JVM Name:        %s%n", props.get("java.vm.name"));
        System.out.printf("JVM Version:     %s%n", props.get("java.vm.version"));
        System.out.printf("JVM Info:        %s%n", props.get("java.vm.info"));
        System.out.printf("Runtime Name:    %s%n", props.get("java.runtime.name"));
        System.out.printf("Runtime Version: %s%n", props.get("java.runtime.version"));
        System.out.printf("Java Home:        %s%n", props.get("java.home"));
        System.out.printf("Class Path:      %s%n", props.get("java.class.path"));

        // Show if running from JDK or JRE
        String javaHome = props.get("java.home").toString();
        boolean hasJavac = new java.io.File(javaHome + "/bin/javac").exists();
        System.out.printf("Has Compiler (JDK): %b%n", hasJavac);
    }
}

Output on a machine with only JRE installed:

=== Java Environment ===
Java Version:    17.0.9
JVM Vendor:      Eclipse Adoptium
JVM Name:        OpenJDK 64-Bit Server VM
JVM Version:     17.0.9+9
Runtime Name:    OpenJDK Runtime Environment
Has Compiler (JDK): false   ← This machine has JRE only!

Observability Checklist

  • Metrics: JVM vendor, version, and bitness (32/64-bit)
  • Logs: Java startup flags and environment variables on application boot
  • Traces: java -XshowSettings:all -version captures all JVM settings
  • Alerts: Version mismatch between build-time JDK and runtime JRE
  • Health Check: java -version and javac -version must match for builds

Security and Compliance Notes

  • Download JDK/JRE only from trusted sources — Oracle, Eclipse Temurin (Adoptium), Azul, or Amazon Corretto
  • Java 8 update 201+ requires paid license for Oracle JDK — migrate to OpenJDK or Temurin to avoid costs
  • JCE (Java Cryptography Extension) unlimited strength jurisdiction files may be required for AES-256 — install separately in some JDK versions
  • Critical patches — Oracle releases quarterly patches; track via https://java.com/psupatch or adopt a JDK with automatic updates (Amazon Corretto, Azul Zulu)

Common Pitfalls and Anti-Patterns

PitfallWhy It HurtsFix
Installing JRE instead of JDK on dev machinesCannot compile or run Maven/Gradle buildsInstall JDK — it includes JRE
JAVA_HOME pointing to JRE instead of JDKjavac missing on command lineVerify $JAVA_HOME/bin/javac exists
Multiple JDK versions without cleanupBuilds use wrong compiler versionUse update-alternatives (Linux) or SDKMAN to manage
Assuming JVM vendor neutralityHotSpot-specific flags fail on OpenJ9Stick to standard JVM flags
Downloading JDK from third-party sitesBundled malware in installerUse official java.com, adoptium.net, or aws.amazon.com/corretto

Quick Recap Checklist

  • JVM — The specification and implementation that executes bytecode; defines memory model, threading, and instruction set
  • JRE — JVM + core class library (rt.jar); sufficient to run Java applications
  • JDK — JRE + development tools (javac, jar, jdb, javadoc); needed to write and compile Java
  • JDK includes JRE; JRE includes JVM — layered containment
  • HotSpot, OpenJ9, GraalVM, and Azul Zing are different JVM implementations
  • OpenJDK is the open-source reference implementation; Oracle JDK is a commercial derivative
  • Development machines need JDK; production servers typically need only JRE (or minimal custom runtime)
  • Verify java -version and javac -version match to avoid version mismatch errors
  • Use jlink to create minimal custom runtime images for containerized deployments

Interview Q&A

What is the difference between JDK, JRE, and JVM?

The JVM (Java Virtual Machine) is the runtime engine that executes bytecode — it interprets or JIT-compiles .class files to native machine instructions. The JRE (Java Runtime Environment) packages the JVM with the core class library (rt.jar) needed to run Java applications. The JDK (Java Development Kit) adds development tools like javac, javadoc, jdb, and jar on top of the JRE. If you are writing code, you need a JDK. If you are only running an app, the JRE suffices.

Can you run Java programs with only a JRE installed? What about JDK?

Yes — a JRE alone can run any compiled Java application (bytecode in .class or .jar files). The java launcher is part of the JRE. However, you cannot compile Java source code with only a JRE because javac is a JDK tool. A JDK includes the JRE, so with a JDK installed you can both compile and run Java programs.

Name three different JVM implementations and explain their key differences.

HotSpot (Oracle, Eclipse Temurin) is the most widely used — it features tiered compilation with aggressive JIT optimizations and the widest range of GC algorithms (Serial, Parallel, G1, ZGC, Shenandoah). OpenJ9 (Eclipse) emphasizes fast startup and lower memory footprint, originally from IBM's J9 JVM. GraalVM (Oracle) adds ahead-of-time (AOT) compilation via native image, plus polyglot support for running JavaScript, Python, and Ruby alongside Java.

Why do we need the JVM instead of just compiling Java directly to machine code?

The JVM provides platform independence — the same .class bytecode runs on Windows, Linux, macOS, and embedded devices without recompilation. It also offers memory safety (no dangling pointers), security sandboxing for untrusted code, and dynamic class loading. The JVM's abstraction lets developers focus on portable code while JVM implementers optimize for each platform's hardware. JIT compilation at runtime can also apply CPU-specific optimizations that static compilation cannot anticipate.

How do you create a minimal Java runtime image for a containerized application?

Use the jlink tool to assemble a custom runtime image containing only the modules your application requires. First, determine your application's module dependencies with jdeps --print-deps myapp.jar. Then run: jlink --add-modules java.base,java.logging,java.sql --output myruntime. This produces a minimal image with only the specified modules, dramatically reducing size — useful for Docker containers where a full JDK/JRE would be wasteful.

Further Reading

6. Why does the JDK include a JRE if JRE is meant for end users?

The JDK ships as a superset of the JRE because developers also need to run applications during development — debugging, testing, and executing sample code are daily activities. Including the JRE means a single JDK installation handles both roles. You can think of it as: JDK = JRE + compilation tools. If you install only the JRE, the javac binary is simply not present in $JAVA_HOME/bin.

7. What is the difference between a JDK and a JRE in terms of file size and use case?

A JRE is a stripped-down runtime — it contains the JVM, core class library (rt.jar), and native libraries. A JDK adds the compiler (javac), debugger (jdb), profiler, documentation generator (javadoc), and build tools. In practice, JDK installations are 2–3x larger because they include all development binaries. For production servers running only compiled applications, the JRE (or a custom jlink image) is preferred to reduce footprint and attack surface.

8. What are the main JDK editions and when would you use each?

JDK SE (Standard Edition) is for general-purpose desktop and server applications — it is what most developers install. JDK EE (Enterprise Edition) was deprecated in 2018; Jakarta EE is its successor for enterprise middleware and web services. JDK ME (Micro Edition) targets mobile devices, embedded systems, and IoT with a stripped-down class library and a smaller footprint VM. Most backend developers work exclusively with JDK SE, using Maven or Gradle for dependency management.

9. Can you compile Java code on a machine that has only the JRE installed?

No — the Java compiler (javac) is a JDK tool, not a JRE tool. Attempting to run javac on a JRE-only machine produces bash: javac: command not found. The JRE only includes the java launcher and runtime library. For any compilation, debugging, or build activity, you need the JDK installed.

10. What does the JVM specification define, and what does it leave to implementers?

The JVM Specification defines the abstract runtime engine — bytecode instruction set, runtime memory model (heap, stack, PC registers, metaspace), thread synchronization primitives, and class file format. It does not mandate specific implementations of memory allocator, garbage collection algorithm, JIT compilation strategy, or thread scheduling. This is why HotSpot, OpenJ9, GraalVM, and Azul Zing all behave identically for the same bytecode but differ in performance characteristics, memory usage, and startup time.

11. What is the relationship between OpenJDK and Oracle JDK?

OpenJDK is the open-source reference implementation of the Java SE specification, released under GPL v2 with the Classpath exception. Oracle JDK is Oracle's commercially supported distribution built on top of OpenJDK — it adds proprietary tools like Java Flight Recorder, Java Mission Control, and GraalVM Enterprise. For most use cases, OpenJDK (via Eclipse Temurin, Amazon Corretto, or Azul Zulu) is sufficient and free. Oracle JDK is chosen when commercial support and Oracle's additional tools are needed.

12. How does jlink create a smaller Java runtime image, and why would you use it?

jlink assembles a custom Java runtime containing only the modules your application actually needs. You first use jdeps --print-deps myapp.jar to discover required modules, then run: jlink --add-modules java.base,java.logging,java.sql --output myruntime. The output is a minimal directory with a stripped-down JVM and only the JARs you depend on — often 30–60% smaller than a full JRE. This is ideal for containerized microservices where a full JDK/JRE would waste megabytes and expand the attack surface.

13. What is the difference between JDK 17's source/target compatibility flags and actual JVM compatibility?

The javac -source and -target flags control which Java language version the compiler accepts and which bytecode version it emits. For example, -target 11 produces bytecode that any JVM 11+ can execute, regardless of whether the host JDK is 17. However, the runtime JVM must support features used — a Java 17 class file using string switches (introduced in JDK 14) will fail on a JDK 11 JVM. The bytecode version number (major.minor) is the real compatibility gate, not the -target flag alone.

14. Why does GraalVM native image have faster startup but slower peak performance compared to HotSpot JIT?

GraalVM native image pre-compiles bytecode to machine code during the build phase via AOT (ahead-of-time) compilation, eliminating JVM startup and JIT warmup time. However, AOT compilation cannot apply runtime profiling data — it cannot do speculative inlining based on actual call frequencies or deoptimize and re-optimize based on runtime behavior. HotSpot's JIT compiler observes the running program and applies aggressive optimizations (inlining, scalar replacement, lock elision) that often outperform static AOT code for long-running, stable workloads.

15. What does setting JAVA_HOME do, and what happens if it points to a JRE instead of a JDK?

JAVA_HOME is an environment variable that points to the JDK or JRE installation directory. Many build tools (Maven, Gradle, Ant), IDEs (IntelliJ, Eclipse), and scripts use $JAVA_HOME/bin/java or $JAVA_HOME/bin/javac to locate the Java runtime. If JAVA_HOME points to a JRE instead of a JDK, javac will be missing — you will see bash: javac: command not found when building. Tools that rely on the compiler will fail, even though java (runnable from the JRE) would still work.

16. What is the purpose of the Java bytecode verifier?

The bytecode verifier is a critical security component that runs before any compiled code is executed. It checks that the .class file's instruction sequences are valid — ensuring no stack overflow, no invalid type casts, no accessing fields of objects that don't exist, and no violation of access modifiers. This prevents malicious .class files from crashing the JVM or bypassing the security sandbox. The verifier is why untrusted Java code cannot corrupt memory or escalate privileges — it is fundamentally impossible to compile a .class file that passes verification but contains unsafe operations.

17. How do you check whether a running Java process was launched from a JDK or a JRE?

Two approaches: (1) Check if javac exists in $JAVA_HOME/bin — if it does, it is a JDK. (2) From within the Java program itself, use new java.io.File(System.getProperty("java.home") + "/bin/javac").exists(). This is what the implementation snippet in this post demonstrates. Build servers and CI pipelines often use this check to fail-fast if a developer accidentally configured a JRE-only environment for a project that needs compilation.

18. What is the difference between JDK 8, 11, 17, and 21 LTS releases in practical terms?

Each LTS release removes deprecated APIs, adds new language features, and ships updated JVM implementations. JDK 8 introduced lambda expressions and the streams API. JDK 11 removed the Java EE module (JAX-WS, JAXB) and added HTTP Client (standardized in JDK 11). JDK 17 added sealed classes and pattern matching for switch. JDK 21 added virtual threads (project Loom) and record patterns. Newer LTS versions also include substantially improved GC algorithms (ZGC and Shenandoah became production-ready), better metaspace handling, and faster JIT compilers — upgrading the JVM version often yields free performance gains.

19. What is the classpath, and how does it differ from the module path introduced in Java 9?

The classpath is a list of directories and JAR files that the JVM searches for class files at runtime. It is an unbounded, flat list — every JAR is examined in order until the class is found. The module path (Java 9+) enforces explicit module boundaries: each JAR must declare its module-info.java, and the module system validates reads and exports at compile and runtime. The module path prevents accidental cross-boundary dependencies and enables jlink to create minimal images. Legacy JARs without module-info still work on the classpath in "automatic module" mode, but lose the encapsulation benefits.

20. Can multiple JDK versions coexist on the same machine, and how do you switch between them?

Yes — multiple JDK versions can be installed simultaneously in separate directories (e.g., /usr/lib/jvm/java-11 and /usr/lib/jvm/java-17). On Linux, update-alternatives --config java selects the system-wide default. On macOS, Oracle's JDK installer manages /Library/Java/JavaVirtualMachines/. SDKMAN! (sdk install java 17.0.9-temurin) and jenv are popular version managers that let you switch per-project via a .java-version file. Always ensure java -version and javac -version reference the same installation to avoid version mismatch errors.

Summary

JDK, JRE, and JVM are three distinct layers that serve different purposes — the JVM executes bytecode, the JRE provides the runtime library support, and the JDK equips you to build. Knowing which you need prevents wasted disk space on dev machines and production errors from version mismatches. With this layered model in mind, you can make informed choices about JVM implementations and deployment strategies.

Next: Now that you know the difference between JDK, JRE, and JVM, explore Java Primitive Types to understand how the JVM represents and manipulates fundamental data values.

Category

Related Posts

Abstract Classes in Java

Learn about partially implemented classes that define contracts for subclasses using abstract methods and concrete implementations.

#java-abstract-classes #java #java-fundamentals

Arithmetic Operators in Java

Master Java arithmetic operators: addition, subtraction, multiplication, division, and modulo with integer division gotchas and operator precedence explained.

#java-arithmetic-operators #java #java-fundamentals

Array Basics in Java

Learn Java array fundamentals: declaration, initialization, element access, and the length property explained simply.

#java-array-basics #java #java-fundamentals