Skip to main content

JVM Architecture

Reading Time: 10 Minutes
Difficulty: Intermediate


Topic Summaryโ€‹

The Java Virtual Machine (JVM) is the engine that runs your Java programs. It reads compiled Java bytecode and executes it on your operating system. Because of the JVM, you can write Java code once and run it on any machine โ€” Windows, Mac, Linux โ€” without changing a single line.


What You'll Learnโ€‹

  • The major components of the JVM and their roles
  • How Java achieves platform independence ("Write Once, Run Anywhere")
  • How ClassLoader, Runtime Data Areas, Execution Engine, and Native Interface work together

Prerequisitesโ€‹

  • Basic understanding of Java programs (writing and compiling)
  • What a .class file is

Explanationโ€‹

What is the JVM?โ€‹

When you write Java code, the Java compiler (javac) converts your .java files into .class files. These .class files contain bytecode โ€” a special intermediate language that is NOT specific to any operating system. The JVM then reads this bytecode and translates it into machine-specific instructions that your computer understands.

This is why Java is called platform-independent: the bytecode is the same everywhere, but each operating system has its own JVM that knows how to run it locally.

Your Java Code (.java)
โ†“ [javac compiler]
Bytecode (.class) โ† Platform Independent
โ†“ [JVM]
Machine Code โ† Platform Specific

The Big Picture: JVM Architecture Diagramโ€‹

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ JVM โ”‚
โ”‚ โ”‚
โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
โ”‚ โ”‚ Class Loader Subsystem โ”‚ โ”‚
โ”‚ โ”‚ Bootstrap โ†’ Extension โ†’ Application โ”‚ โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
โ”‚ โ†“ โ”‚
โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
โ”‚ โ”‚ Runtime Data Areas โ”‚ โ”‚
โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ Method โ”‚ โ”‚ Heap โ”‚ โ”‚ Stack (per thread)โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ Area โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚
โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ PC Register โ”‚ โ”‚ Native Method Stack โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
โ”‚ โ†“ โ”‚
โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
โ”‚ โ”‚ Execution Engine โ”‚ โ”‚
โ”‚ โ”‚ Interpreter + JIT Compiler + GC โ”‚ โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
โ”‚ โ†“ โ”‚
โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
โ”‚ โ”‚ Native Method Interface (JNI) โ”‚ โ”‚
โ”‚ โ”‚ Native Method Libraries (C/C++) โ”‚ โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Component 1: ClassLoader Subsystemโ€‹

The ClassLoader is responsible for loading .class files into the JVM memory. It does this in three steps:

  1. Loading โ€” Reads the .class file from the disk (or network) and brings it into memory.
  2. Linking โ€” Verifies the bytecode is valid, allocates memory for static variables, and resolves symbolic references.
  3. Initialization โ€” Executes static initializer blocks and assigns values to static variables.

There are three built-in ClassLoaders arranged in a hierarchy:

  • Bootstrap ClassLoader โ€” Loads core Java classes (java.lang, java.util, etc.)
  • Extension ClassLoader โ€” Loads classes from the ext directory
  • Application ClassLoader โ€” Loads your application's classes from the classpath

We'll cover ClassLoaders in much more detail in the next lesson.


Component 2: Runtime Data Areasโ€‹

This is the memory of the JVM. It's divided into several regions, each serving a different purpose:

AreaShared?Stores
Method AreaAll threadsClass metadata, static variables, method bytecode
HeapAll threadsObjects, instance variables
StackPer threadMethod call frames, local variables
PC RegisterPer threadAddress of current instruction
Native Method StackPer threadNative (C/C++) method calls

Each area has a specific job. We'll explore them deeply in Lesson 3.


Component 3: Execution Engineโ€‹

The Execution Engine is the part that actually runs the bytecode. It has three key parts:

Interpreterโ€‹

Reads and executes bytecode instructions one at a time. Simple but slow โ€” it re-interprets the same instructions every time they run.

JIT Compiler (Just-In-Time Compiler)โ€‹

The JIT compiler watches which methods are called frequently ("hot methods") and compiles them directly into native machine code. This native code is cached and reused, making the program run much faster after warmup.

Cold path: Bytecode โ†’ Interpreter โ†’ executes slowly
Hot path: Bytecode โ†’ JIT Compiler โ†’ Native Code โ†’ executes very fast

Garbage Collector (GC)โ€‹

Automatically frees memory by removing objects that are no longer used. You don't need to manually free memory in Java โ€” the GC does it for you. More on this in Lessons 4 and 5.


Component 4: Native Method Interface (JNI)โ€‹

Sometimes Java needs to interact with code written in other languages like C or C++. For example, reading from hardware, calling OS-level functions, or using existing native libraries.

The Java Native Interface (JNI) is the bridge that allows Java code to call native methods written in C/C++. The Native Method Libraries are the actual C/C++ .dll or .so files that contain those native implementations.

You'll see native keyword used in Java for this:

public native void someNativeMethod(); // implemented in C/C++

Platform Independence Explainedโ€‹

The key insight is:

  • Java source code is compiled to bytecode โ€” this bytecode is the SAME on all platforms.
  • The JVM is platform-specific โ€” there's a different JVM for Windows, Mac, and Linux.
  • The JVM knows how to translate the same bytecode to the correct machine code for each OS.

So Java's slogan "Write Once, Run Anywhere" means: write bytecode once, and the platform-specific JVM handles the rest.


Real-World Analogyโ€‹

Think of Java bytecode like a PDF document. The PDF looks the same regardless of what computer you're on. But you need a PDF reader (like Adobe Reader) that is specific to your operating system โ€” Windows uses a Windows PDF reader, Mac uses a Mac PDF reader. The JVM is like the PDF reader, and your Java bytecode is like the PDF.


Code Exampleโ€‹

// This simple program goes through the entire JVM pipeline
public class JVMDemo {

// Static variable โ€” stored in Method Area
static int counter = 0;

public static void main(String[] args) {
// Local variable โ€” stored on the Stack
int localVar = 10;

// Object โ€” stored in the Heap
String message = new String("Hello, JVM!");

// This bytecode is interpreted/JIT compiled by the Execution Engine
for (int i = 0; i < 5; i++) {
counter++;
System.out.println(message + " Counter: " + counter);
}

System.out.println("Local var: " + localVar);
}
}

Outputโ€‹

Hello, JVM! Counter: 1
Hello, JVM! Counter: 2
Hello, JVM! Counter: 3
Hello, JVM! Counter: 4
Hello, JVM! Counter: 5
Local var: 10

Common Mistakesโ€‹

  • โŒ Mistake: Thinking the JVM is platform-independent โ†’ โœ… Fix: The JVM itself is platform-specific. It's the bytecode that is platform-independent.
  • โŒ Mistake: Confusing JVM, JRE, and JDK โ†’ โœ… Fix: JDK (compiler + tools) โŠƒ JRE (JVM + standard libraries) โŠƒ JVM (just the runtime engine).
  • โŒ Mistake: Thinking the Interpreter and JIT Compiler are the same โ†’ โœ… Fix: Interpreter runs bytecode line by line; JIT compiles hot code to native for speed.

Best Practicesโ€‹

  • Always use the latest LTS JDK to benefit from JVM improvements (better JIT, better GC).
  • Understand that JVM warmup time exists โ€” JIT compilation takes time before the app reaches peak speed.
  • Use JVM flags to tune memory for your application's needs (covered in Lesson 6).
  • Don't call System.gc() manually โ€” trust the Garbage Collector.

Interview Questionsโ€‹

Q: What is the JVM and why is Java platform-independent?
A: The JVM (Java Virtual Machine) is a runtime environment that executes Java bytecode. Java is platform-independent because the Java compiler produces bytecode (.class files) that is the same on all platforms. Each OS has its own JVM that translates bytecode into OS-specific machine code. So the bytecode is portable; the JVM is not.

Q: What is the difference between JDK, JRE, and JVM?
A: JDK (Java Development Kit) includes the compiler (javac), debugging tools, and the JRE. JRE (Java Runtime Environment) includes the JVM plus the standard class libraries needed to run Java programs. JVM (Java Virtual Machine) is just the engine that loads and executes bytecode. JDK โŠƒ JRE โŠƒ JVM.

Q: What is JIT compilation and why does it matter?
A: JIT (Just-In-Time) compilation is performed by the JVM's execution engine. It identifies frequently executed ("hot") bytecode and compiles it into native machine code, caching it for future use. This makes Java programs run much faster after initial warmup compared to pure interpretation.

Q: What are the main components of the JVM?
A: The four main components are: (1) ClassLoader Subsystem โ€” loads .class files into memory; (2) Runtime Data Areas โ€” memory regions like Heap, Stack, Method Area; (3) Execution Engine โ€” Interpreter, JIT Compiler, and Garbage Collector; (4) Native Method Interface (JNI) โ€” bridge to native C/C++ libraries.


Quick Revisionโ€‹

โœ” JVM is the engine that runs Java bytecode; it is platform-specific
โœ” Java bytecode is platform-independent โ€” compiled once, runs everywhere
โœ” ClassLoader loads .class files; Runtime Data Areas store data; Execution Engine runs code
โœ” JIT Compiler converts hot bytecode to native machine code for performance
โœ” JNI allows Java to call native C/C++ code


  • ClassLoader Subsystem (Lesson 2)
  • JVM Memory Areas (Lesson 3)
  • Garbage Collection (Lesson 4)

Next Lessonโ€‹

Lesson 2 โ€” ClassLoader: How Java Loads Your Classes