What you'll learn
You've been running on the JVM this whole time — now let's look under the hood. Understanding how the JVM loads classes, manages memory, and collects garbage helps you write efficient code and diagnose the tough bugs: leaks, out-of-memory errors, and performance mysteries.
By the end you'll be able to:
- Describe the JVM's architecture and class loading
- Explain the difference between the stack and the heap
- Understand garbage collection and reference types
- Recognise the causes of memory leaks,
OutOfMemoryError, andStackOverflowError - Understand what the JIT compiler does
JVM architecture & class loading
The JVM is the engine that runs your bytecode (Module 1). When your program references a class for the first time, a class loader finds its .class file, verifies the bytecode is safe, and loads it into memory — lazily, only when needed. Loaders form a hierarchy (bootstrap → platform → application), each delegating upward before trying itself.
Class metadata lives in a region called Metaspace (outside the heap). The two memory areas you'll think about most, though, are the stack and the heap.
Stack vs heap
This distinction explains a huge amount of Java's behaviour — including pass-by-value (Module 6) and why objects are shared. Every thread has its own stack for method calls and local variables; all threads share one heap where objects live:
The Stack
- • One per thread
- • Holds method call frames
- • Local variables & primitives
- • Object references (the arrows)
- • Fast; freed automatically when a method returns
The Heap
- • Shared by all threads
- • Holds every object & array
- • Where
newallocates - • Cleaned by the garbage collector
- • Larger and longer-lived
void demo() {
int count = 5; // primitive -> lives on the STACK
String name = "Sara"; // reference on stack, String on HEAP
int[] data = new int[1000]; // reference on stack, array on HEAP
}
// When demo() returns, the stack frame (count, name, data) is gone.
// The objects on the heap stay until no references point to them.Key idea
Garbage collection
Unlike C, you never manually free memory in Java. The garbage collector (GC) automatically reclaims heap objects that are no longer reachable — nothing references them anymore. It works on a simple insight: most objects die young, so the heap is split into young and old generations that are collected at different rates.
Note
Reference types & memory leaks
The GC decides what to keep based on how objects are referenced. Beyond ordinary (strong) references, Java offers weaker kinds for caches and cleanup:
| Reference | GC behaviour | Use for |
|---|---|---|
Strong | Never collected while reachable | normal references (the default) |
Soft | Collected only when memory is low | memory-sensitive caches |
Weak | Collected at the next GC | canonical maps, listeners |
Phantom | For pre-cleanup actions | advanced resource management |
"Automatic" memory doesn't mean "leak-proof." A memory leak in Java happens when you keep references to objects you no longer need — so the GC can't reclaim them. The usual culprit is a long-lived collection that only grows:
import java.util.*;
// A classic leak: a static collection that only ever grows.
class Cache {
static final List<byte[]> HELD = new ArrayList<>();
static void add() {
HELD.add(new byte[1_000_000]); // never removed -> heap fills up
}
}Memory errors
OutOfMemoryError— the heap is full and the GC can't free enough (often a leak, or too small a heap)StackOverflowError— the call stack is exhausted, almost always from unbounded recursion
static int boom(int n) {
return boom(n + 1); // no base case -> stack frames pile up
}
// Calling boom(0) throws StackOverflowErrorWatch out
Errors, not exceptions — you don't catch them, you fix the cause: remove the leak, add a recursion base case, or right-size the heap with the -Xmx flag.JIT compilation
The JVM starts by interpreting bytecode, but it watches which methods run often ("hot" code). The Just-In-Time (JIT) compiler then compiles those hot paths to native machine code and optimises them aggressively — which is why Java reaches near-C speed after a brief warm-up. This is also why micro-benchmarks are tricky (Module 38): the first runs are slow, then the JIT kicks in.
Recap & quick check
Key takeaways
- Class loaders lazily load, verify, and link classes; metadata lives in Metaspace.
- The stack holds method frames, locals, and references (one per thread); the heap holds objects (shared).
- Garbage collection automatically frees unreachable heap objects — no manual free().
- Leaks come from keeping references you no longer need; StackOverflowError from unbounded recursion.
- The JIT compiles hot methods to native code after a warm-up, giving Java its speed.
Quick check
1. Where are objects created with 'new' stored?
2. What does the garbage collector reclaim?
3. What usually causes a StackOverflowError?
4. Can Java programs still have memory leaks?
5. What does the JIT compiler do?
Excellent — you now understand what happens beneath your code. Next up: Module 28 — Modern Java Language Features, the finale of Phase 4.