JVM Memory Areas
The JVM doesn't use one big block of memory; it divides memory into several areas, each with its own job. Your objects live in the heap. Each thread has a stack. Class information lives in metaspace. Compiled machine code lives in the code cache. Knowing these areas helps you size the JVM correctly and understand messages such as OutOfMemoryError: Metaspace. In this lesson, you will learn each memory area and how to look at it from a running program.
The Memory Areas
| Area | Holds | Shared? | Size set by | Error when full |
|---|---|---|---|---|
| Heap | All objects and arrays (young and old generations) | Yes | -Xms, -Xmx | OutOfMemoryError: Java heap space |
| Thread stacks | Frames, local variables, operand stacks | No, one per thread | -Xss | StackOverflowError |
| Metaspace | Class metadata: fields, methods, bytecode, constant pools | Yes | -XX:MaxMetaspaceSize (unlimited by default) | OutOfMemoryError: Metaspace |
| Code cache | Machine code produced by the JIT compiler | Yes | -XX:ReservedCodeCacheSize | JIT stops compiling (a warning) |
| Direct memory | ByteBuffer.allocateDirect buffers | Yes | -XX:MaxDirectMemorySize | OutOfMemoryError: Direct buffer memory |
| Program counter | The current instruction of each thread | No | Fixed | None |
Before Java 8, class metadata lived in an area of the heap called PermGen, which caused frequent "PermGen space" errors. Metaspace replaced it and lives in native memory.
Example: Listing the Memory Pools
Let's see an example that asks the running JVM for its memory pools.
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryType;
public class MemoryPools {
public static void main(String[] args) {
for (MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) {
String type =
pool.getType() == MemoryType.HEAP ? "heap " : "non-heap";
System.out.println(type + " " + pool.getName());
}
}
}non-heap CodeHeap 'non-nmethods'
non-heap Metaspace
non-heap CodeHeap 'profiled nmethods'
non-heap Compressed Class Space
heap G1 Eden Space
heap G1 Old Gen
heap G1 Survivor Space
non-heap CodeHeap 'non-profiled nmethods'Explanation: The heap appears as three pools of the default G1 garbage collector: Eden and Survivor (the young generation) and Old Gen. Outside the heap are Metaspace, the Compressed Class Space (part of metaspace for class pointers), and three parts of the code cache ("CodeHeap"), which hold JIT-compiled code at different stages. With another collector, the heap pool names change.
Example: Heap Usage Before and After Allocating
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
public class HeapUsage {
public static void main(String[] args) {
MemoryMXBean memory = ManagementFactory.getMemoryMXBean();
System.gc();
long before = memory.getHeapMemoryUsage().getUsed();
int[][] blocks = new int[50][];
for (int i = 0; i < blocks.length; i++) {
blocks[i] = new int[250_000];
}
System.gc();
long after = memory.getHeapMemoryUsage().getUsed();
long grewMb = (after - before) / (1024 * 1024);
System.out.println("Heap grew by at least 40 MB? " + (grewMb >= 40));
System.out.println("Non-heap is in use too? "
+ (memory.getNonHeapMemoryUsage().getUsed() > 0));
System.out.println(blocks.length + " blocks kept alive");
}
}Heap grew by at least 40 MB? true
Non-heap is in use too? true
50 blocks kept aliveExplanation: Fifty arrays of 250,000 ints are about 50 MB, all on the heap. The non-heap areas (metaspace, code cache) are in use too, holding the program's classes and compiled code. System.gc() is called before both measurements so that leftover garbage doesn't hide the growth; without it, a collection that happens in between can make the heap look smaller than it is. The exact numbers change from run to run, so the program checks a range.
Common Mistakes
Thinking -Xmx caps the whole process. Many areas live outside the heap.
Looking for PermGen in modern Java. It was removed in Java 8.
Ignoring thread stacks. Thousands of platform threads can use gigabytes of stack memory.
Interview Corner
What are the memory areas of the JVM?
The heap stores all objects and arrays and is divided by the garbage collector into young (eden and survivor) and old generations; it's shared by all threads and sized with -Xms/-Xmx. Each thread has a stack of frames for method calls and a program counter. Metaspace, in native memory since Java 8 (replacing PermGen), stores class metadata. The code cache stores JIT-compiled native code. There's also other native memory, such as direct byte buffers and GC data structures.
Practice
- Easy: Run
MemoryPoolswith-XX:+UseSerialGCand with-XX:+UseZGC, and compare the heap pool names. - Harder: Load the same class with ten new
URLClassLoaders and watch metaspace usage grow withMemoryPoolMXBean.
Key Takeaways
Was this tutorial helpful?