JVM & Memory

96. JVM Runtime Data Areas

The JVM divides memory into the Heap, Metaspace, JVM Stacks, Program Counter (PC) Registers, and Native Method Stacks; understanding their boundaries is essential for tuning and diagnosing OutOfMemoryErrors.

Java 21+ (HotSpot architecture)

Explanation

The HotSpot Java Virtual Machine divides runtime memory into shared and per-thread memory areas. The shared areas exist across the entire JVM lifecycle and are shared among all application threads. The Heap stores all class instances and arrays (subject to GC). Metaspace (which replaced PermGen in Java 8) stores loaded class metadata, runtime constant pools, and method bytecodes in native OS memory rather than on the managed Java heap.

Each Java thread has its own private runtime areas. The JVM Stack stores activation stack frames for each method invocation, holding local variables, operand stacks, and frame data. The PC Register holds the address of the currently executing JVM instruction for that thread. The Native Method Stack supports C/C++ or JNI code invoked via native methods.

Diagnosing errors requires matching the memory area to the symptom. java.lang.OutOfMemoryError: Java heap space means the heap cannot allocate space for an object even after full GC. java.lang.OutOfMemoryError: Metaspace occurs when too many classes are generated or loaded dynamically (e.g., CGLIB, reflection, uncollected class loaders). java.lang.StackOverflowError indicates infinite recursion or an excessively deep call chain exceeding -Xss.

Example

import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryUsage;

public class JvmMemoryAreas {
    public static void main(String[] args) {
        MemoryMXBean memBean = ManagementFactory.getMemoryMXBean();

        // 1. Heap Memory (Young Gen + Old Gen)
        MemoryUsage heap = memBean.getHeapMemoryUsage();
        System.out.printf("Heap Init: %d MB, Used: %d MB, Max: %d MB%n",
            heap.getInit() / (1024 * 1024),
            heap.getUsed() / (1024 * 1024),
            heap.getMax()  / (1024 * 1024));

        // 2. Non-Heap Memory (Metaspace, Code Cache, Compressed Class Space)
        MemoryUsage nonHeap = memBean.getNonHeapMemoryUsage();
        System.out.printf("Non-Heap Used: %d MB, Max: %d MB%n",
            nonHeap.getUsed() / (1024 * 1024),
            nonHeap.getMax()  / (1024 * 1024));

        // 3. Inspect individual memory pools
        System.out.println("\nMemory Pools:");
        for (MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) {
            System.out.printf("  - %-30s [%s] Used: %d KB%n",
                pool.getName(),
                pool.getType(),
                pool.getUsage().getUsed() / 1024);
        }
    }
}

Sources