Java Virtual Machine specification defines multiple runtime memory regions utilized during program execution. Certain regions are created at JVM startup and persist until termination, while others are dedicated to individual threads. Thread-specific areas are instantiated upon thread creation and released when threads terminate.
Memory Regions Overview
Process Lifecycle Regions: Data within these areas is shared across all threads, requiring synchronization mechanisms for concurrent access.
Method Area
The Method Area represents a shared memory region among all threads, established during JVM initialization. Though the specification conceptually positions it within the heap as a logical component, it's distinctly referred to as Non-Heap to differentiate it from the regular object heap space.
Implementation evolved across JDK versions:
- JDK 6/7: Perm Space (Permanent Generation)
- JDK 8+: Metaspace
This region stores class-level metadata including loaded class structures, runtime constants, static variables, and JIT-compiled code caches. Memory allocation failures trigger OutOfMemoryError exceptions.
Constant Pools and Runtime Constant Pools
Constant Pool: A static table structure embedded in .class files that enables bytecode instructions to locate class names, method signatures, parameter types, and literal values (strings, primitives). This resides on disk storage.
Runtime Constant Pool: When a class loads, its constant pool data is transferred into the runtime constant pool in memory. Symbolic references are resolved into actual memory addresses during this transformation.
The resolution process converts symbolic references (e.g., hexadecimal identifiers like 4f62 6a65 6374) into direct pointers (e.g., 0x00ab-0x00ba) that reference physical memory locations. This conversion occurs lazily—symbols remain unresolved until the executing code specifically references them.
String Interning Behavior
Where are String constants stored?
Pre-Java 7: Method Area
Post-Java 7: Heap Memory
This relocation improved garbage collection efficiency, as heap collection occurs more frequently than method area cleanup.
String Creation Patterns
class StringMemoryTest {
public static void main(String[] arguments) {
String first = "java";
String second = "java";
String third = new String("java");
String fourth = third.intern();
System.out.println(first.equals(second)); // true
System.out.println(first == second); // true
System.out.println(first.equals(third)); // true
System.out.println(first == third); // false
System.out.println(first.equals(fourth)); // true
System.out.println(first == fourth); // true
}
}
The intern() method checks the string pool for an existing value, returning the pooled reference if found or adding the string to the pool and returning its reference.
Heap Memory
The Java Heap represents the largest managed memory segment, created at JVM startup and shared by all threads. All object instances and arrays allocate memory here. Insufficient heap space results in OutOfMemoryError.
Object Memory Layout
Each Java object occupies memory in three segments:
- Object Header: Contains mark word (hashcode, GC age, lock state) and class pointer
- Instance Data: Stores actual field values
- Padding Alignment: Ensures 8-byte boundary alignment for efficient memory access
Cross-Region References
Method Area to Heap: Class metadata in the method area references corresponding Class objects allocated on the heap.
Heap to Method Area: Object headers contain pointers to their class metadata stored in the method area.
Java Virtual Machine Stacks
Each thread maintains a private JVM Stack that tracks method invocation states. Stack frames are pushed for each method call and popped upon completion.
Stack Frame Composition
Every stack frame contains:
- Local Variable Table: Stores method parameters and local variables
- Operand Stack: Executes bytecode instructions via push/pop operations
- Dynamic Linking: Resolves symbolic method references
- Return Address: Stores the instruction pointer for returning control
- Reference to Runtime Constnat Pool: Links to the class's constant pool
Execution Flow Demonstration
void processRequest(){
validateInput();
}
void validateInput(){
executeQuery();
}
void executeQuery(){
// Database operation implementation
}
The call sequence processRequest → validateInput → executeQuery results in three stack frames being pushed sequentially, then popped in reverse order.
Bytecode Execution Example
public static java.lang.Integer addValues(java.lang.Integer, java.lang.Integer);
descriptor: (Ljava/lang/Integer;Ljava/lang/Integer;)Ljava/lang/Integer;
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=2
0: iconst_5 // Push int constant 5 to operand stack
1: invokestatic #7 // Call Integer.valueOf:(I)Ljava/lang/Integer;
4: astore_0 // Store result in local variable 0
5: aload_0 // Load local variable 0 onto stack
6: invokevirtual #8 // Call Integer.intValue:()I
9: aload_1 // Load parameter 1 onto stack
10: invokevirtual #8 // Call Integer.intValue:()I
13: iadd // Pop top two ints, add them, push result
14: invokestatic #7 // Call Integer.valueOf:(I)Ljava/lang/Integer;
17: astore_2 // Store result in local variable 2
18: aload_2 // Load local variable 2 onto stack
19: areturn // Return Integer object from method
Local Variable Indexing
Local variable indexing depends on method type:
Static Methods: Parameters begin at index 0
Instance Methods: Index 0 holds the this reference; parameters start at index 1
The JVM specification mandates this layout to ensure consistent parameter passing.
Stack-to-Heap References
Local variables of reference types contain pointers to objects on the heap. This establishes the fundamental connection between stack execution context and heap-allocated data.
Object Access Mechanisms
The JVM specification doesn't prescribe exact object location strategies, allowing implementation flexibility. Two primary aproaches exist:
Handle-Based Access
The heap allocates a dedicated handle pool where references point to handle structures rather than direct object addresses. Each handle contains two pointers:
- Object instance data location
- Type metadata location (in method area)
Advantages: Stable reference values. Object relocation during garbage collection only requires updating the handle's instance pointer, leaving the reference unchanged.
Instance vs Type Data:
- Instance data resides in heap (object fields)
- Type data lives in method area (class structure, static fields)
Handle Example:
Customer clientRef; // Reference handle
Customer client = new Customer(); // Handle bound to instance
client.setId(1001); // Object manipulation via handle
Direct Pointer Access
References directly store object addresses. The heap object layout must embed type data access mechanisms within the object structure.
Advantages: Faster access by eliminating one indirection level. This performance benefit becomes significant given the high frequency of object access operations.
HotSpot Implementation: Uses direct pointers with class metadata addresses stored in object headers, making it the predominant modern approach.
Native Method Stacks
Native method stacks serve as the equivalent of JVM stacks for native code execution. When Java code invokes platform-specific native methods (via JNI), these stacks manage native execution contexts.
Program Counter Register
Each thread maintains a private PC Register that tracks the current execution address:
- Java Methods: Stores the index of executing bytecode instructions
- Native Methods: Contains undefined value (
null)
This compact memory area enables thread scheduling, method interruption, and resumption by preserving execution context. The register size is negligible compared to other memory regions.