Mastering JVM Memory: From Heap Layout to Garbage Collection Tuning
This article provides a comprehensive guide to JVM internals, covering runtime data areas, heap segmentation, object allocation, reference types, class loading phases, garbage collection algorithms, collector choices across JDK versions, performance tuning commands, and practical troubleshooting techniques for production Java applications.
JVM Memory Layout
The JVM runtime data area consists of the heap, method area (Metaspace in JDK 8+), virtual machine stack, native method stack, and program counter.
Heap Details
The heap stores object instances and arrays, is shared among threads, and is the primary region for garbage collection. It is divided into young and old generations; the young generation further contains Eden, Survivor 1, and Survivor 2 spaces.
Method (Metaspace) Area
Stores class metadata, constants, and static variables. Since JDK 8 the term "method area" is replaced by Metaspace. Excessive class loading (e.g., by frameworks like Spring) can cause Metaspace overflow.
Virtual Machine Stack
Thread‑private stack holding stack frames, each containing a local variable table, operand stack, dynamic linking information, and return address. StackOverflowError occurs when the stack depth exceeds limits; OutOfMemoryError occurs when the stack cannot be expanded.
Local Variable Table : Stores method parameters and local variables.
Operand Stack : Records push/pop operations of bytecode instructions.
Dynamic Linking : Resolves symbolic references to direct references at runtime.
Return Address : Points to the next bytecode instruction after method return.
Native Method Stack
Similar to the VM stack but serves native methods; in HotSpot it is merged with the VM stack and also throws StackOverflowError and OutOfMemoryError.
Program Counter (PC)
Thread‑private pointer to the next instruction to execute.
Object Allocation Strategy
Objects are allocated in Eden; if Eden lacks space, a Minor GC is triggered. Surviving objects move to Survivor spaces; after reaching a certain age (default 15) they are promoted to the old generation. Large objects are allocated directly in the old generation to avoid copying overhead.
Object Creation Steps
Class loading check
Memory allocation (pointer bumping or free‑list)
Zero‑value initialization
Object header setup (class pointer, hash, GC age, lock flags)
Execution of the init method
Object Reference Types
Strong Reference : Default reference type.
Soft Reference : Collected only when memory is low.
Weak Reference : Collected on any GC cycle.
Phantom Reference : Used to track object finalization.
JVM Class Loading Process
The phases are loading, verification, preparation, resolution, and initialization.
Loading Phase
Read the binary byte stream of the class.
Convert the byte stream into a runtime data structure in the method area.
Create a java.lang.Class object in the heap.
Verification Phase
File format verification
Metadata verification
Bytecode verification
Symbolic reference verification
Preparation Phase
Allocate memory for static variables and set them to default values.
Resolution Phase
Replace symbolic references in the constant pool with direct references.
Initialization Phase
Execute the class’s static initializers and <clinit> method.
Class Loader Mechanisms
Java uses the parent‑delegation model: a class loader first delegates loading to its parent before attempting to load the class itself. This ensures core classes are loaded first and prevents duplicate loading.
Breaking the delegation (e.g., custom class loaders, Tomcat’s WebAppClassLoader) can lead to multiple versions of the same class and potential conflicts.
Tomcat Class Loading
Check local cache for the class.
If not found, check the system class loader cache.
Attempt loading with ExtClassLoader (bypassing AppClassLoader).
If still not found, use BootstrapClassLoader.
Finally, fall back to AppClassLoader or throw an exception.
This deliberately breaks the parent‑delegation model to prioritize web‑app classes.
JVM Garbage Collection
Reference Counting (Deprecated)
Simple but cannot handle cyclic references.
Reachability Analysis
Starts from GC roots (threads, static fields, JNI refs) and marks reachable objects; unreachable objects are reclaimed.
Mark‑Sweep, Mark‑Compact, and Generational Collection
Copying algorithm (young generation)
Mark‑Sweep (CMS, old generation)
Mark‑Compact (Serial Old, Parallel Old)
Generational collection selects algorithms based on object age.
Garbage Collectors by JDK Version
JDK 3 : Serial (single‑threaded), ParNew (multithreaded copy collector).
JDK 5 : Parallel Scavenge (throughput‑oriented), Serial Old.
JDK 8 : CMS (low‑pause concurrent collector), Parallel Old.
JDK 9 : G1 (predictable pause times, region‑based).
JDK 11 : ZGC (concurrent, low‑pause, supports up to TB‑scale heaps).
JDK 12 : Shenandoah (pause‑independent).
JDK 13‑14 : ZGC expanded to macOS/Windows, CMS removed.
GC Configuration Tips
Set heap size limits (e.g., 2/3 of system memory).
Adjust young/old generation ratios based on workload.
Use tools like GCeasy to analyze GC logs.
JVM Performance Tuning Commands
jps– List Java processes. jinfo – Show JVM flags. jstat – Monitor GC and memory statistics. jstack – Thread dump (detect deadlocks, long‑running threads). jmap – Heap dump and histogram. jhsdb – Advanced debugging (heap, core files).
JDK New Features
JDK 8: Lambda expressions, Stream API, HashMap improvements.
JDK 9: Default G1 collector, enhanced Stream API.
JDK 10: Parallel full GC improvements.
JDK 11: ZGC with sub‑millisecond pauses.
JDK 12: Shenandoah GC.
JDK 13: Larger heap support for ZGC.
JDK 14: Removal of CMS, deprecation of ParallelScavenge+SerialOld combo.
Online Fault Diagnosis
Typical steps: isolate the faulty instance, preserve the state, then investigate.
Network: ss -antp Network stats: netstat -s Process resources: lsof -p $PID CPU: mpstat, vmstat, sar -p ALL I/O: iostat -x Memory: free -h Global info: ps -ef, dmesg, sysctl -a Heap dump: jmap -dump:format=b,file=heap.bin $PID Thread dump: jstack $PID Core dump:
gcore -o core $PIDCommon Issues and Solutions
Memory leaks from unbounded caches – replace with Guava Cache using weak references.
File‑handle leaks – ensure streams are closed in finally blocks.
High CPU due to GC – analyze object age distribution, tune MaxTenuringThreshold, enable ParallelRefProcEnabled, consider G1 with pause target.
Swap usage causing long GC pauses – disable swap on production servers.
By applying these tuning steps and monitoring practices, JVM performance can be stabilized even under heavy load.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Alibaba Cloud Developer
Alibaba's official tech channel, featuring all of its technology innovations.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
