Definition
The JVM divides runtime memory into distinct regions, each with a specific job. The Heap holds all objects and is shared across every thread, and it is internally split into the Young Generation (newly created objects) and the Old Generation (long-lived objects). Each thread gets its own Stack holding local variables and method call frames. Metaspace (which replaced PermGen in Java 8) stores class metadata in native memory. The Code Cache stores native machine code produced by the Just-In-Time compiler.
Understanding the Concepts
What are the JVM memory areas?
The JVM organizes runtime memory into several distinct regions, each serving a specific purpose. Understanding these regions is essential for tuning, troubleshooting, and performance optimization in production banking systems.
The Heap is the largest memory region and the only place where objects live. When you write new Account("SAV2001"), the JVM allocates that object on the heap. Because the heap is shared by all threads, it is the region the garbage collector manages. The heap is split into the Young Generation and the Old Generation. The Young Generation itself has three parts: Eden (where brand-new objects are born) and two Survivor spaces called S0 and S1. Objects that keep surviving garbage collections eventually get promoted into the Old Generation.
How does the Stack work?
Each thread has its own Stack. Every time a method is called, a new stack frame is pushed holding that method's local variables, parameters, and the return address. For example, when transfer() calls debit(), a frame for debit() is pushed on top. When debit() returns, its frame is popped. Primitive locals like int count live directly on the stack, while object references on the stack point to objects on the heap.
package com.bank.service;
import java.math.BigDecimal;
public class TransferService {
public void transfer(Account from, Account to, BigDecimal amount) {
// Stack frame for transfer(): holds from, to, amount references
validate(amount);
from.debit(amount);
to.credit(amount);
}
private void validate(BigDecimal amount) {
// Stack frame for validate(): holds amount reference
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("Amount must be positive");
}
}
}
The reference from sits on the stack, but the actual Account object it points to sits on the heap. If recursion goes too deep, the stack overflows and you get a StackOverflowError. You control stack size with -Xss.
What is Metaspace?
Metaspace stores class metadata – the structure of each loaded class, method bytecode, and the runtime constant pool. Before Java 8 this lived in a fixed-size region called PermGen inside the heap, which frequently caused OutOfMemoryError: PermGen space in applications that loaded many classes. Java 8 moved it to native memory and renamed it Metaspace, which grows automatically but can be capped with -XX:MaxMetaspaceSize.
# Cap Metaspace to prevent runaway class loading from consuming all native memory
java -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m -jar bank-app.jar
What is the Code Cache?
The Code Cache holds native machine code generated by the JIT compiler. When a method becomes "hot" (called many times), the JIT compiles it from bytecode to native code and stores it here so future calls skip interpretation. If the Code Cache fills up, the JIT stops compiling and performance degrades. You size it with -XX:ReservedCodeCacheSize.
How do you see memory area usage in practice?
You can inspect JVM memory areas at runtime using command-line tools.
# Show heap and non-heap memory usage
jcmd <pid> VM.native_memory summary
# Show GC details including generation sizes
jstat -gc <pid> 1000
# Show loaded class count (Metaspace pressure indicator)
jcmd <pid> VM.classloader_stats
What are the rules and constraints?
| Memory Area | Shared? | Stores | Error on overflow |
|---|---|---|---|
| Heap (Young + Old) | Yes (all threads) | Objects, arrays | OutOfMemoryError: Java heap space |
| Stack | No (per thread) | Locals, frames | StackOverflowError |
| Metaspace | Yes (all threads) | Class metadata | OutOfMemoryError: Metaspace |
| Code Cache | Yes (all threads) | JIT-compiled code | JIT stops compiling |
A senior-level nuance: the Program Counter register and native method stacks are also per-thread JVM areas, but the four regions above are what matter most in interviews and tuning. In banking systems at National Bank of India, Rajesh sizes each region based on the application's profile: large heaps for services that cache account data, modest stacks for high-thread-count API servers, and Metaspace caps to catch class-loading leaks early.
Key Takeaway
The JVM organizes runtime memory into four main areas, each with a clear purpose. The Heap is where every object lives and it is shared by all threads, which is why it is the region the garbage collector manages. The Heap is divided into the Young Generation, where new objects are born, and the Old Generation, where long-lived objects are promoted after surviving several collections. Each thread has its own Stack that holds method call frames with their local variables and parameters, which is why one thread's locals can never be seen by another thread. Metaspace, which replaced PermGen in Java 8 and now lives in native memory, holds class metadata like field layouts and method bytecode. The Code Cache holds the native machine code that the Just-In-Time compiler produces for hot methods.
Understanding this split matters because it tells you where a memory problem comes from. If the Heap fills up you get an OutOfMemoryError in heap space and you take a heap dump. If Metaspace fills up, usually from a class-loading leak, you get an OutOfMemoryError in Metaspace. If recursion runs away you get a StackOverflowError on the thread stack. Each symptom points to a different region and a different fix.
In my banking work at National Bank of India, this knowledge is practical every day. Our transfer service creates huge numbers of short-lived request objects that are born and die in the Young Generation, so Minor GC handles them cheaply and frequently. A handful of long-lived objects like the connection pool get promoted to the Old Generation and are collected rarely. When we had a memory issue, knowing the regions let me immediately decide whether to take a heap dump for the Heap or inspect class loading for Metaspace, instead of guessing. That is why I always tune -Xms and -Xmx for the Heap and watch non-heap memory separately in production.
Remember one sentence: What are the JVM memory areas: Heap (objects, shared across threads: Young Gen + Old Gen), Stack (per thread: local variables, method calls), Metaspace (class metadata, replaces PermGen in Java 8+), Code Cache (JIT-compiled native code) is fundamental knowledge for Java interviews and production banking work.
---