Fundamentals 22 min read

Understanding JVM Runtime Data Areas: From JDK 8 to JDK 17

This article walks through the five JVM runtime data areas, explains the distinction between the JVM specification and HotSpot implementation, compares the PermGen to Metaspace transition from JDK 8 to JDK 17, and provides hands‑on code to reproduce each type of memory‑related OutOfMemoryError.

Dabaoshi
Dabaoshi
Dabaoshi
Understanding JVM Runtime Data Areas: From JDK 8 to JDK 17

1. Building a Memory Map

The JVM specification defines five runtime data areas but leaves their concrete implementation to each JVM. HotSpot, the default JVM, implements these areas as follows: program counter, Java virtual stack, native method stack (all thread‑private), and heap plus method area (both shared across threads). Understanding which area is private or shared is the foundation for later topics such as garbage collection and concurrency.

2. Demonstrating the Areas with an Order Example

A simple OrderService program is used to illustrate where each piece of data lives:

public class OrderService {
    // static variable: class‑level data
    private static int totalCount = 0;
    public static void main(String[] args) {
        OrderService service = new OrderService();
        Order order = service.createOrder(1001L, 99); // ← focus this line
        System.out.println(order);
    }
    public Order createOrder(long userId, int amount) {
        Order order = new Order(userId, amount);
        totalCount++;
        return order;
    }
}

When createOrder executes, the program counter records the current bytecode, the virtual stack holds two frames (for main and createOrder) with local variables and the this reference, the newly created Order object resides in the heap, and the class metadata and static totalCount live in the method area.

3. Program Counter

The program counter is a tiny thread‑private memory slot that stores the address of the next bytecode instruction. It never throws an OutOfMemoryError because its size is fixed. For native methods the counter is undefined because execution jumps to native code.

4. Java Virtual Stack and Native Method Stack

4.1 Stack Frames

Each method call creates a stack frame containing a local variable table, an operand stack, a dynamic link to the runtime constant pool, and a return address. Slots for primitive types occupy one entry, while long and double occupy two. In instance methods slot 0 always holds this.

4.2 Stack‑Related Exceptions

StackOverflowError

: caused by exceeding the maximum stack depth, typically by unbounded recursion. OutOfMemoryError (stack): occurs when the JVM cannot allocate a new stack for a newly created thread; HotSpot’s stack size is fixed by -Xss, so this error is usually seen as “unable to create new native thread”.

Tip: the default -Xss on 64‑bit systems is roughly 512 KB–1 MB. Reducing it (e.g., -Xss256k ) makes StackOverflowError appear faster for experiments.

5. Heap

The heap is the largest shared memory region, storing all object instances and arrays. It is the primary target of garbage collection. HotSpot logically divides the heap into Young and Old generations (Eden, Survivor spaces), but the article only notes that such a division exists.

Heap size is controlled by -Xms (initial) and -Xmx (maximum). A common practice is to set them to the same value to avoid costly resizing during runtime.

When the heap cannot accommodate new objects, the JVM throws java.lang.OutOfMemoryError: Java heap space. Detailed analysis of such OOMs is deferred to a later article.

6. Method Area (PermGen → Metaspace)

The method area stores class metadata, the runtime constant pool, and static variables. In JDK 7 and earlier HotSpot used the heap‑resident PermGen; starting with JDK 8 it switched to Metaspace, which allocates native memory instead of heap memory.

Key differences:

Metaspace size is limited only by physical memory unless -XX:MaxMetaspaceSize is set.

PermGen required explicit size parameters ( -XX:PermSize, -XX:MaxPermSize) and frequently caused OOMs.

Typical JVM flags:

# JDK 7 and earlier (PermGen)
-XX:PermSize=128m -XX:MaxPermSize=256m

# JDK 8 and later (Metaspace)
-XX:MetaspaceSize=128m   # initial high‑water mark for class‑unload GC
-XX:MaxMetaspaceSize=256m # optional upper bound; default is unlimited

Because Metaspace can grow until native memory is exhausted, production environments often set a reasonable -XX:MaxMetaspaceSize to surface a OutOfMemoryError: Metaspace before the whole machine runs out of RAM.

6.3 String Constant Pool Migration

Since JDK 7 the string constant pool has been moved from PermGen to the heap, allowing it to benefit from regular garbage collection.

7. Direct Memory

Direct memory (allocated via NIO’s DirectByteBuffer) is not part of the JVM spec but is used by HotSpot. It is controlled by -XX:MaxDirectMemorySize (default equals -Xmx) and can cause java.lang.OutOfMemoryError: Direct buffer memory. Unlike heap OOMs, the Java heap may appear healthy while the process runs out of native memory.

8. Hands‑On Reproducing Each OOM

Three code snippets are provided to trigger the most common OOMs:

Heap OOM – run with -Xms16m -Xmx16m -XX:+HeapDumpOnOutOfMemoryError and continuously allocate 1 MB byte arrays in a live list.

Stack OOM – run with -Xss256k and invoke an infinite recursive method to exceed the stack depth.

Metaspace OOM – run with -XX:MaxMetaspaceSize=32m and repeatedly generate new classes using CGLIB/ASM (cache disabled) to fill Metaspace.

Running the same Metaspace code on JDK 7 with -XX:MaxPermSize=32m produces a java.lang.OutOfMemoryError: PermGen space, illustrating the historical change.

Conclusion

The five runtime data areas are split by a clear private/shared boundary: program counter, virtual stack, and native stack are thread‑private; heap and method area are shared.

Objects live in the heap, references in the stack, and class templates in the method area.

The program counter is the only area that cannot cause OOM.

JDK 8 replaced PermGen with Metaspace, moving the method‑area storage to native memory and eliminating the MaxPermSize limit.

Direct memory, although outside the spec, is a common source of hidden OOMs.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaJVMGarbage CollectionHeapMetaspaceRuntime Data Areas
Dabaoshi
Written by

Dabaoshi

Practical utilities

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.