Fundamentals 15 min read

Unlocking JVM Memory: A Deep Dive into Heap, Metaspace, and Stack Architecture

This article explains the JVM memory layout, covering the heap (including Xms/Xmx tuning, Young/Old generations, and garbage collection), Metaspace versus PermGen, the thread‑private JVM stack and native method stack, and the program counter register, with practical code examples.

IT Architects Alliance
IT Architects Alliance
IT Architects Alliance
Unlocking JVM Memory: A Deep Dive into Heap, Metaspace, and Stack Architecture

Memory Layout Overview

The JVM memory layout defines how Java allocates, manages, and reclaims memory at runtime, ensuring stable and efficient execution. Different JVM implementations may vary slightly, but the core regions are consistent.

Heap

Metaspace

JVM Stacks (virtual machine stack)

Native Method Stacks

Program Counter Register

Heap Region

The Heap stores all Java object instances and is the primary source of OutOfMemoryError (OOM). It is shared among all threads and is usually the largest memory area.

Heap size can be customized at startup and adjusted at runtime using the -Xms (initial heap size) and -Xmx (maximum heap size) parameters. For example, -Xms256M sets the initial heap to 256 MB, while -Xmx1024M caps it at 1 GB.

Because dynamic resizing can cause performance spikes under fluctuating load, it is common practice to set -Xms and -Xmx to the same value, stabilizing heap size and reducing GC‑induced pressure.

The heap is divided into two major regions: Young generation (new generation), consisting of Eden and two Survivor spaces ( S0 and S1). Old generation (tenured generation).

Objects are initially allocated in Eden. When Eden fills, a Young Garbage Collection ( YGC) moves surviving objects to a Survivor space. After a configurable number of Survivor‑to‑Survivor copies (default -XX:MaxTenuringThreshold=15), objects are promoted to the Old generation. If a Survivor space cannot accommodate an object, it is directly promoted.

When both Young and Old generations cannot accommodate a new allocation, a Full Garbage Collection ( FGC) is triggered. If the allocation still fails, the JVM throws an OOM exception.

Metaspace (元空间)

Starting with JDK 8, the former PermGen was replaced by Metaspace, which is allocated in native memory rather than the heap. Unlike PermGen, Metaspace can grow automatically, and its size is controlled by -XX:MetaspaceSize and -XX:MaxMetaspaceSize.

Class metadata—including class names, constant pool, field information, static variables, methods, and bytecode—are stored in Metaspace. This relocation improves tuning flexibility and eliminates the need for full‑heap garbage collection to reclaim class metadata.

JVM Stacks (虚拟机栈)

The JVM stack is a thread‑private LIFO structure that holds stack frames for each method invocation. Each frame contains:

Local Variable Table : stores method parameters and local variables. For non‑static methods, slot 0 holds the this reference.

Operand Stack : used by the bytecode interpreter to perform calculations.

Dynamic Linking Information : a reference to the method’s constant‑pool entry, enabling late binding.

Method Return Address : the point to resume execution after the method returns.

Example bytecode for a simple addition method:

public int add() {
    int x = 10;
    int y = 20;
    int z = x + y;
    return z;
}

Corresponding bytecode sequence:

0: bipush 10          // push constant 10 onto operand stack
2: istore_1          // store into local variable slot 1 (x)
3: bipush 20          // push constant 20
5: istore_2          // store into slot 2 (y)
6: iload_1           // load x onto operand stack
7: iload_2           // load y onto operand stack
8: iadd              // add top two ints, push result
9: istore_3          // store result into slot 3 (z)
10: iload_3          // load z
11: ireturn          // return int value

During YGC, the JVM copies surviving objects between the two Survivor spaces ( S0 and S1) and clears the previously used space, minimizing pause times.

Native Method Stacks (本地方法栈)

The native method stack is also thread‑private and hosts frames for native (JNI) calls. Native methods interact with the JVM via the Java Native Interface, gaining direct access to OS resources. Excessive native calls can bypass JVM safety checks and may lead to native heap OutOfMemory errors.

Program Counter Register (程序计数寄存器)

Each thread maintains its own Program Counter (PC) register, which stores the address of the next bytecode instruction to execute. The PC is thread‑local, never shared, and does not cause memory‑overflow exceptions.

Conclusion

From a thread perspective, the heap and Metaspace are shared across all threads, while the JVM stack, native method stack, and PC register are private to each thread. Understanding these regions helps developers tune memory parameters, avoid OOM scenarios, and write more efficient Java code.

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.

JVMMemory ManagementGarbage CollectionStackHeapMetaspace
IT Architects Alliance
Written by

IT Architects Alliance

Discussion and exchange on system, internet, large‑scale distributed, high‑availability, and high‑performance architectures, as well as big data, machine learning, AI, and architecture adjustments with internet technologies. Includes real‑world large‑scale architecture case studies. Open to architects who have ideas and enjoy sharing.

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.