Mastering JVM Memory and Garbage Collection: A Comprehensive Guide
This article provides an in-depth exploration of Java's JVM memory architecture, garbage collection algorithms, class file structure, class loading process, bytecode execution, and method dispatch mechanisms, offering practical code examples and visual diagrams to help developers understand and optimize JVM performance.
01. Introduction
Notes from reading the third edition of "Deep Understanding of JVM"; the following is a personal review.
02. Java Memory Areas and Memory Overflow
Heap : Created at JVM startup according to -Xmx and -Xms sizes; stores objects and arrays, managed by GC.
Method Area : Stores class metadata, runtime constant pool, and JIT compiled code. In JDK8 it resides in the permanent generation; later versions use Metaspace.
Direct Memory : Introduced in JDK1.4 via NIO; allocated outside the heap using native libraries.
Program Counter : Records the next instruction address for each thread.
JVM Stack
Native Method Stack
03. Runtime Data Areas
Thread Local Memory
Program Counter
JVM Stack : Stores stack frames with local variables, operand stack, and return address.
Native Method Stack
04. JVM Objects
Object Creation : Heap memory is allocated based on class size; each thread gets a Thread‑Local Allocation Buffer (TLAB) to reduce contention. byte[] buf1 = new byte[MB/16]; Object Layout
Mark Word : Stores hash code, GC age, lock state.
Class Pointer : Points to class metadata.
Array Length (if applicable).
Object Data : Fields stored sequentially.
Alignment Padding : Ensures memory alignment.
public class User {
private int age = -1;
private String name = "unknown";
}
// JOL output shows object header, class pointer, fields, and padding.05. Memory Overflow
Heap overflow occurs when -Xmx is exceeded, throwing OutOfMemoryError. Use -XX:+HeapDumpOnOutOfMemoryError and tools like Eclipse MAT to locate leaking objects.
Stack overflow occurs when -Xss limit is exceeded.
Metaspace overflow can be triggered by excessive class loading; control with -XX:MetaspaceSize and -XX:MaxMetaspaceSize.
06. Garbage Collection and Memory Allocation
GC solves three sub‑problems: which memory to collect, when to collect, and how to collect.
07. GC Conditions
Reference Counting : Simple but cannot handle cycles.
Reachability Analysis : Starts from GC roots (thread stacks, static fields, JNI handles) and marks reachable objects.
08. Reference Types
Strong Reference
Soft Reference ( SoftReference) – collected only when memory is low.
Weak Reference ( WeakReference) – collected eagerly.
Phantom Reference ( PhantomReference) – enqueued after finalization.
09. GC Algorithms
Mark‑Sweep : Marks unreachable objects and sweeps them; can cause fragmentation.
Mark‑Copy : Copies live objects to a survivor space; eliminates fragmentation but incurs copy overhead.
Mark‑Compact : Moves live objects to one side of the heap and compacts free space.
10. Classic Garbage Collectors
Serial : Single‑threaded, pauses all application threads.
Parallel (Throughput) Collector : Multi‑threaded, aims for high throughput.
CMS : Concurrent Mark‑Sweep; reduces pause times but may suffer from floating garbage.
G1 : Region‑based collector with configurable pause targets; uses concurrent marking and region‑based evacuation.
11. Memory Allocation Strategies
Objects are first allocated in Eden; large objects may be allocated directly in the old generation using -XX:PretenureSizeThreshold. Objects that survive several young‑gen GCs are promoted based on -XX:MaxTenuringThreshold and survivor space occupancy.
12. Class File Structure
A .class file consists of a magic number, version, constant pool, access flags, this and super class indices, interfaces, fields, methods, and attributes.
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}Key constant pool entries include CONSTANT_Utf8_info, CONSTANT_Class_info, CONSTANT_Fieldref_info, CONSTANT_Methodref_info, and CONSTANT_NameAndType_info.
13. Bytecode Instructions
JVM bytecode operates on an operand stack and local variable array. Instructions are grouped into load/store, arithmetic, type conversion, object creation, method invocation, control flow, and others.
// Load and store examples
iload_0
istore_1
bipush 10
ldc "hello"
// Arithmetic examples
iadd
isub
imul
idiv
// Method invocation
invokevirtual #5 // Method f:()V
invokespecial #3 // Constructor
invokestatic #7 // Static method14. Class Loading Process
The JVM performs loading, verification, preparation, (optional) resolution, and initialization. Static fields are allocated and zero‑initialized during preparation; constant static fields are initialized during preparation.
During initialization, the class initializer <clinit>() runs, which may trigger loading of dependent classes.
15. Class Loaders
Class loaders follow the parent‑delegation model: a loader first delegates to its parent; if the parent cannot find the class, the loader attempts to load it from its classpath.
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
Class<?> c = findLoadedClass(name);
if (c == null) {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
if (c == null) {
c = findClass(name);
}
}
if (resolve) resolveClass(c);
return c;
}
}16. Bytecode Execution Engine
Each method invocation creates a stack frame containing a local variable array, an operand stack, and a return address. The operand stack depth is fixed at compile time.
public static void main(String[] args) {
int a = 1008611;
int b = ++a;
}
// Stack frame: locals[2], operand stack depth 217. Method Dispatch
Static (compile‑time) dispatch resolves overloaded methods based on the compile‑time types of arguments. Dynamic (run‑time) dispatch resolves overridden instance methods based on the actual object type using invokevirtual or invokeinterface.
class StaticDispatch {
public void f(Human h) { System.out.println("f(Human)"); }
public void f(Man m) { System.out.println("f(Man)"); }
public static void main(String[] args) {
Human h = new Man(); // static type Human
new StaticDispatch().f(h); // prints f(Human) – static dispatch
}
}
class DynamicDispatch {
abstract class Human { abstract void f(); }
class Man extends Human { void f() { System.out.println("Man f()"); } }
class Woman extends Human { void f() { System.out.println("Woman f()"); } }
public static void main(String[] args) {
Human h = new Man();
h.f(); // prints Man f() – dynamic dispatch
}
}18. Summary
The article reviews JVM memory regions, garbage collection strategies, class file format, class loading, bytecode execution, and method dispatch, providing a solid foundation for developers to understand and tune Java applications.
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.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
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.
