memory management
intermediateJVM Memory Areas
Understand where Java puts objects (heap), call frames (stack), and class metadata (metaspace) so you can diagnose OOMs.
The JVM divides memory into separate areas, and each holds a different kind of thing. Knowing which area holds what is how you read an OutOfMemoryError, because the message names the area.
Heap is a warehouse for products (objects). Stack is each worker's clipboard (per-thread locals). Metaspace is the rulebook section (class blueprints).
Key Concepts
1
The heap holds objects. Every object you create with new lives here, and every thread shares it. It
is the biggest area and the one -Xmx sizes.
new-Xmx
2
Each thread has its own stack, and it holds variables, not objects. When you call a method, a frame is
pushed onto that thread's stack with its local variables. When the method returns, the frame is thrown
away.
3
The distinction that matters:
4
void example() {
int count = 5; // the number 5 is ON THE STACK
String name = new String("x"); // the OBJECT is on the heap;
} // the reference to it is on the stack5
This is why thread count is a memory decision. Each thread reserves its own stack, typically half a
megabyte to a megabyte, and that memory is outside the heap.
6
200 threads ~200 MB
1,000 threads ~1 GB
5,000 threads ~5 GB -- before your objects use anything7
That is the real reason thread pools are bounded. It is also why virtual threads, added in Java 21, matter:
their stacks live on the heap and grow only as needed, so a million of them is possible.
8
Metaspace holds class definitions, not objects. It sits outside the heap in native memory and, unless
you cap it, grows until the operating system refuses.
9
The usual cause of it filling up is class loaders that are never released — frameworks generating classes
at runtime, or an application server where each redeploy loads everything again.
10
Read the error message; it tells you where to look.
"Java heap space" objects are being retained
-> take a heap snapshot
"Metaspace" classes are being retained
-> look for class loader leaks
"unable to create new native
thread" too many threads, or an OS limit
"Direct buffer memory" off-heap NIO buffers not released
StackOverflowError a call chain too deep for the
stack -- usually runaway recursion,
and raising -Xmx does nothing11
One caveat to "objects live on the heap". The JIT compiler can sometimes prove an object never escapes
the method that created it, and then it may put it on the stack or remove it entirely. So the rule is true
at the language level and not always true at runtime.
12
What the interviewer is probing.1. "Where do objects and local variables live?" Probing: the split. Stalls: "Objects on the heap."
Moves up: objects on the shared heap, locals and references in per-thread frames.
13
2. "Heap error or metaspace error?" Probing: reading the message. Stalls: "Both mean out of
memory." Moves up: retained objects versus retained classes, and where each points you.
14
3. "Why does 1,000 threads cost a gigabyte?" Probing: stack arithmetic. Stalls: "Threads are
heavy." Moves up: each reserves up to a megabyte outside the heap.
15
4. "What causes StackOverflowError?" Probing: telling it apart from OOM. Stalls: "Running out of
memory." Moves up: a call chain deeper than the thread's stack; more heap does not help.
StackOverflowError
16
5. "Why generational collection?" Probing: the idea behind it. Stalls: "It is faster." Moves up:
most objects die almost immediately, so collecting the young area often reclaims most garbage cheaply.
17
Memory areas18
Area Shared? Holds OOM message
------------------------------------------------------------------
Heap all threads objects, arrays "Java heap space"
Eden new allocations
Survivor S0/S1 minor-GC survivors
Old gen promoted, long-lived "Java heap space"
Stack per thread frames: locals, operand StackOverflowError
stack, return address
Metaspace all threads class metadata (native) "Metaspace"
Code cache all threads JIT-compiled machine code "CodeCache"
Direct/NIO all threads off-heap ByteBuffers "Direct buffer
memory"19
Thread count is a memory decision20
default stack per thread (-Xss) 512 KB - 1 MB
200 threads ~200 MB
1,000 threads ~1 GB
5,000 threads ~5 GB (before heap)21
-> this is why pools are bounded, and why virtual threads
(Java 21) matter: their stacks live on the heap and grow
on demand, so a million of them is feasible.22
Reading the error
Java heap space -> objects retained; take a heap dump
Metaspace -> classes retained; check classloaders
and dynamic proxy generation
unable to create new
native thread -> thread count or OS ulimit
Direct buffer memory -> NIO buffers not released
StackOverflowError -> recursion depth, not heap size23
Key flags
-Xmx / -Xms max / initial heap
-Xss stack size per thread
-XX:MaxMetaspaceSize cap metaspace (unbounded by default)
-XX:+HeapDumpOnOutOfMemoryError always set this in production