Definition
Java has three layers. Each one has exactly one responsibility.
- JVM is the engine that runs your compiled code.
- JRE is the engine plus all the standard library classes your program needs.
- JDK is everything in the JRE plus the tools you need to write and compile code.
They are nested like boxes inside boxes. The JDK is the biggest box. Open it and you find the JRE. Open the JRE and you find the JVM.
Think of a restaurant. The JVM is the stove that cooks the food. The JRE is the stove plus the pots, pans, spices, and recipe books the chef needs. The JDK is the entire kitchen including the stove, the cookware, and also the knives, cutting boards, and measurement tools used to prepare ingredients. A customer just eats (JRE is enough). The chef needs the full kitchen (JDK) to create the dish.
Understanding the Concepts
What is the JVM?
The JVM stands for Java Virtual Machine. It is a program that reads compiled Java bytecode and runs it on your computer.
When you write Java code, you do not write instructions for your specific CPU. Instead, the Java compiler produces .class files containing bytecode, which is a set of instructions that any JVM can understand. The JVM then translates those instructions into native machine code for whatever operating system it is running on.
The JVM handles several important jobs:
- Class loading reads your
.classfiles from disk and loads them into memory. - Bytecode verification checks that the loaded code is safe and properly structured before running it.
- Memory management allocates space on the heap for objects and on the stack for method calls.
- Garbage collection automatically frees memory from objects your program no longer uses.
- JIT compilation detects code that runs frequently (hot code) and compiles it directly to native machine code so it runs faster.
The JVM is defined by a specification, not by a single product. HotSpot from Oracle and OpenJ9 from Eclipse are two popular implementations. Both follow the same specification and run the same bytecode, but they use different garbage collection algorithms and JIT strategies under the hood.
What is the JRE?
The JRE stands for Java Runtime Environment. It is the JVM plus everything a Java program needs to run.
The JVM alone can execute bytecode, but your program also needs the standard library classes. When you write String name = "Pankaj" or List, those classes come from the JRE's standard libraries. Without the JRE, the JVM would have an engine but no fuel.
The JRE includes:
- The JVM itself.
- Core libraries like
java.lang,java.util,java.io,java.math, andjava.time. - Security policy files, timezone data, and character encoding tables.
- The
javacommand that starts the JVM.
The JRE cannot compile source code. It can only run programs that have already been compiled into .class files.
What is the JDK?
The JDK stands for Java Development Kit. It contains everything in the JRE plus the tools a developer needs to build Java software.
The most important tool in the JDK is javac, the Java compiler. It reads your .java source files and produces .class bytecode files. Without javac, you cannot turn your code into something the JVM can run.
Other JDK tools include:
jarpackages your compiled classes into a single.jararchive for distribution.javadocgenerates HTML documentation from comments in your source code.jdbis the Java debugger for stepping through code.jshellis an interactive console where you can try out Java code without creating a file (available from Java 9).jlinkbuilds a custom runtime image containing only the modules your application needs (available from Java 9).javapdisassembles.classfiles so you can inspect the bytecode.
You can check which tools are available by running this in your terminal:
# Check if you have the JDK (javac is a JDK-only tool)
javac -version
# Check which JVM and Java version you have
java -version
Why does Java have three separate layers?
The separation exists because different people need different things.
A developer writing code needs the compiler, the debugger, and the packaging tools. A production server running the compiled application does not need any of those tools. It only needs the JVM and the standard libraries to execute the bytecode.
By separating the layers, Java allows you to deploy only what is necessary. This matters for:
- Container size. A Docker image with just the JRE is 200+ MB smaller than one with the full JDK.
- Security. Fewer tools on a production server means a smaller attack surface. An attacker who gains access to a server with only the JRE cannot compile or debug code on that machine.
- Startup time. Smaller runtime images load faster.
How does code travel from writing to running?
Here is the complete journey of a Java program from source code to execution:
- You write
TransferDemo.javain a text editor or IDE. - You run
javac TransferDemo.java. This is a JDK tool. It reads your source code and producesTransferDemo.classcontaining bytecode. - You run
java TransferDemo. Thejavacommand is part of the JRE. It starts the JVM. - The JVM's class loader finds
TransferDemo.classon the classpath and loads it into memory. - The bytecode verifier checks that the loaded code is safe.
- The interpreter begins executing bytecode instructions one by one.
- When the JVM detects that a method is called frequently, the JIT compiler compiles that method to native machine code so future calls run at full hardware speed.
- While the program runs, the garbage collector periodically frees memory from objects that are no longer reachable.
// You can check which JVM is running your program
public class CheckRuntime {
public static void main(String[] args) {
System.out.println("JVM: " + System.getProperty("java.vm.name"));
System.out.println("Vendor: " + System.getProperty("java.vm.vendor"));
System.out.println("Version: " + System.getProperty("java.version"));
System.out.println("Home: " + System.getProperty("java.home"));
}
}
JVM: OpenJDK 64-Bit Server VM
Vendor: Eclipse Adoptium
Version: 17.0.9
Home: /usr/lib/jvm/temurin-17-jdk
Who needs which one?
The rule is simple:
- If you write Java code, you need the JDK. It gives you the compiler and all the development tools.
- If you build Java code on a CI/CD server, that server needs the JDK because it runs
javacandjarduring the build. - If you only run a compiled Java application, you need the JRE or a custom runtime image. You do not need the compiler or debugger.
In practice, most developers install the JDK on their machine and never install the JRE separately, because the JDK already contains the JRE.
What changed after Java 11?
Before Java 11, Oracle shipped two separate downloads: the JDK for developers and the JRE for end users.
Starting with Java 11, Oracle stopped publishing a standalone JRE. If you need a runtime without the development tools, you use jlink to build a custom image:
# Create a slim runtime with only the modules your app uses
jlink --add-modules java.base,java.sql,java.logging \
--output my-app-runtime
# The result is a self-contained runtime, often 40-60 MB
# instead of the 300+ MB full JDK
This change encourages developers to ship only what their application needs. The result is smaller Docker images, faster startup times, and a smaller attack surface in production.
What should a senior engineer know?
- HotSpot vs OpenJ9. Both implement the JVM spec. HotSpot focuses on peak throughput with its C2 JIT compiler. OpenJ9 focuses on fast startup and lower memory footprint. Your compiled
.jarruns on both without any changes. - JIT compilation tiers. HotSpot uses two compilers internally. C1 compiles quickly with basic optimizations. C2 compiles slowly but produces highly optimized native code. Hot methods start on C1 and get promoted to C2 automatically.
- The classloader chain. Bootstrap ClassLoader loads core classes like
java.lang.String. Platform ClassLoader loads standard extensions. Application ClassLoader loads your application code. Understanding this chain helps you debugClassNotFoundExceptionandNoClassDefFoundError. - GraalVM. A newer JVM implementation that supports ahead-of-time compilation into native binaries. This eliminates JVM startup time entirely, making it popular for serverless functions and CLI tools.
- Module system (Java 9+). The JDK itself is divided into modules like
java.base,java.sql, andjava.desktop. Thejlinktool uses this module structure to create custom runtimes containing only the modules your application depends on.
Visual Overview
Source code (.java)
│
│ javac (JDK tool)
▼
Bytecode (.class)
│
│ java launcher (JRE)
▼
┌─────────────────────────────────┐
│ JVM │
│ 1. Class Loader → loads .class │
│ 2. Verifier → checks safe │
│ 3. Interpreter → runs code │
│ 4. JIT Compiler → speeds up │
│ 5. GC → frees memory │
└─────────────────────────────────┘
┌──────────────────────────────────────────┐ │ JDK │ │ javac, jar, javadoc, jdb, jshell, jlink │ │ │ │ ┌──────────────────────────────────┐ │ │ │ JRE │ │ │ │ java.lang java.util java.math │ │ │ │ java.io java.time java.sql │ │ │ │ │ │ │ │ ┌──────────────────────────┐ │ │ │ │ │ JVM │ │ │ │ │ │ Class Loader │ │ │ │ │ │ Bytecode Verifier │ │ │ │ │ │ Memory (Heap + Stack) │ │ │ │ │ │ Execution Engine (JIT) │ │ │ │ │ │ Garbage Collector │ │ │ │ │ └──────────────────────────┘ │ │ │ └──────────────────────────────────┘ │ └──────────────────────────────────────────┘
Key Takeaway
Imagine you have a recipe written on paper. The JVM is the stove that actually cooks the food. The JRE is the stove plus all the pots, pans, and spices you need. The JDK is the full kitchen including the knives and cutting boards used to prepare the ingredients.
When you write Java code, you use the JDK's compiler to turn your .java source into .class bytecode. When you deploy the application, the production server needs only the JRE to run that bytecode. Inside the JRE, the JVM does the real work: it loads your bytecode, verifies it, manages memory, collects garbage, and uses JIT compilation to make hot code run at native speed.
The three layers exist so you can deploy only what you need. A developer installs the full JDK. A build server uses the JDK to compile and package. A production server runs a slim JRE or a custom jlink image with just the modules the application uses.
Remember one sentence: the JDK builds it, the JRE runs it, and the JVM executes it.