Interviewers love this because it checks whether you can separate where data lives from how code runs inside the JVM.
Question: Explain Heap, Stack, Metaspace, PC Register, and Native Method Stack.
Answer: Java does not use one big memory bucket; the JVM divides runtime memory into areas with different jobs. The heap stores objects and is shared by all threads, while each thread gets its own stack for method calls, local variables, and return information. Metaspace holds class metadata, the PC register tracks the current bytecode instruction for each thread, and the native method stack supports calls into native code through JNI.
Interview-Ready Answer: I’d explain that the JVM splits memory by purpose. The heap stores objects and is shared across threads, the stack is per-thread and stores stack frames for local variables and return info, Metaspace stores class metadata in native memory, the PC register keeps each thread’s current instruction, and the native method stack supports JNI and other native calls. A useful detail is that since Java 8, class metadata moved from PermGen to Metaspace.
Big picture: think of the JVM as a busy office. Some things are shared resources, and some are personal workspaces for each thread.
| Area | Shared? | Stores | Typical failure |
|---|---|---|---|
| Heap | Yes | Objects | OutOfMemoryError: Java heap space |
| Stack | No | Frames, locals | StackOverflowError |
| Metaspace | Yes | Class metadata | OutOfMemoryError: Metaspace |
| PC Register | No | Next instruction | Rarely fails directly |
| Native Method Stack | No | Native frames | Native stack overflow |
Metaspace. In Java 8+, this is in native memory, not the Java heap.stack and PC register. These are private to that thread, so one thread’s method calls do not overwrite another’s.new, the object is created on the heap. The reference to that object may live in a stack frame, in another object, or inside a collection.native method stack. On many JVMs this is backed by the operating system thread stack, but conceptually the JVM treats it as a separate area.-Xms, -Xmx, and collector choices.StackOverflowError. A common default on HotSpot is around 1 MB per thread, but it varies by platform and is adjustable with -Xss.-XX:MaxMetaspaceSize. This is the modern replacement for the old PermGen area that existed before Java 8.Memory hook: Heap = shared warehouse, Stack = personal desk, Metaspace = blueprint archive, PC register = bookmark, Native stack = translator room.
Real-world story: Imagine a checkout service in an e-commerce system. Each request thread uses the stack for validation calls, the heap for carts and payment objects, and Metaspace for loaded framework classes. A team once added a custom class-loader-based plugin system and created new loaders per request; class metadata kept piling up in Metaspace until the pods started throwing OutOfMemoryError: Metaspace, followed by repeated restarts and 500 errors. In another incident, a deeply nested address validator caused StackOverflowError because the recursion depth was much larger than the thread stack size.
The symptoms were easy to miss at first: rising memory usage, GC logs with frequent full collections, then sudden request failures, timeouts, and log lines that mentioned either metaspace exhaustion or stack overflow. The fix was to reuse class loaders, cap and monitor Metaspace, and rewrite the recursive validator into an iterative loop.
import java.util.ArrayList;
import java.util.List;
public class MemoryAreasDemo {
// Objects live on the heap. Keeping references here prevents early GC,
// which makes the heap usage visible in a simple demo.
private static final List<byte[]> HEAP_OBJECTS = new ArrayList<>();
private static int depthCounter = 0;
public static void main(String[] args) throws InterruptedException {
printHeapStats("Start");
allocateOnHeap();
printHeapStats("After allocating 10 MB on the heap");
// Thread stacks are per-thread. A small stack size hint helps the demo
// reach StackOverflowError faster, but the JVM is allowed to treat it as a hint.
Thread stackThread = new Thread(null, () -> {
try {
recurse();
} catch (StackOverflowError e) {
System.out.println("Stack demo: caught " + e.getClass().getSimpleName()
+ " after about " + depthCounter + " recursive calls.");
}
}, "stack-demo", 64 * 1024);
stackThread.start();
stackThread.join();
// Metaspace is where class metadata lives. Java code can observe class loading,
// but it does not directly manage the metadata area.
System.out.println("Metaspace note: class metadata is not stored on the Java heap.");
// The PC register is conceptual JVM state: Java cannot read it directly.
System.out.println("PC register note: each thread tracks its next bytecode step internally.");
// Native method stack is used when the JVM calls native code.
System.out.println("Native stack note: JNI/native calls use native frames managed outside Java objects.");
HEAP_OBJECTS.clear();
System.gc();
Thread.sleep(200);
printHeapStats("After clearing references and requesting GC");
}
private static void allocateOnHeap() {
for (int i = 0; i < 10; i++) {
// Each array is a heap object. The local variable 'i' is on the stack,
// while the array data itself is allocated on the heap.
HEAP_OBJECTS.add(new byte[1024 * 1024]);
}
System.out.println("Heap demo: stored 10 arrays of 1 MB each in a shared list.");
}
private static void recurse() {
depthCounter++;
// A few locals make the stack frame a little larger, which helps show that
// every method call consumes per-thread stack space.
long a = depthCounter;
long b = a + 1;
long c = b + 1;
long d = c + 1;
if ((a + b + c + d) == Long.MIN_VALUE) {
System.out.println("Unreachable");
}
recurse();
}
private static void printHeapStats(String label) {
Runtime rt = Runtime.getRuntime();
long used = rt.totalMemory() - rt.freeMemory();
System.out.printf("%s - heap used: %.2f MB, total: %.2f MB, max: %.2f MB%n",
label,
used / 1024.0 / 1024.0,
rt.totalMemory() / 1024.0 / 1024.0,
rt.maxMemory() / 1024.0 / 1024.0);
}
}
Follow-up & Tricky Questions:
-XX:MaxMetaspaceSize.-Xms and -Xmx; stack is tuned with -Xss. If you have many threads, a smaller stack can save memory, but too small a stack increases the risk of StackOverflowError.OutOfMemoryError: Metaspace. This often points to class-loader leaks, excessive dynamic proxy generation, or redeployments that keep old class loaders alive.String object is on the heap; only the reference to it may be in a stack frame. String literals are interned, but they are still managed as heap objects in modern JVMs.Common Mistakes:
StackOverflowError and heap OOM. Correction: Deep recursion usually overflows the stack; too many live objects usually exhaust the heap.Memory Hook: Shared warehouse, personal desk, blueprint archive, bookmark, translator room. If you can recall that line, you can reconstruct the five JVM areas under pressure.
Cheat Sheet:
Heap = shared object memory, managed by GC.Stack = per-thread method frames, locals, and returns.Metaspace = class metadata in native memory; Java 8+.PC register = per-thread current instruction pointer.Native method stack = native/JNI call frames.-Xmx, -Xms, -Xss, -XX:MaxMetaspaceSize.Practice Tasks:
StackOverflowError appears.