Java Memory Management: Beyond the Basics

While Java handles memory automatically via Garbage Collection (GC), high-performance engineering requires understanding the allocation path, reclamation barriers, and internal heap structures.

I. The Allocation Path: TLABs and Bump-the-Pointer

Most developers assume allocation happens on a global heap lock. In reality, the JVM uses Thread-Local Allocation Buffers (TLABs) to avoid contention.

II. Generational Mechanics and Barriers

The Generational Hypothesis states that most objects die young. Modern GCs use Write Barriers to track cross-generational references.

III. Modern GC Comparison: G1 vs. ZGC

FeatureG1 GC (Balanced)ZGC (Latency Focused)
Heap StructureRegions (fixed size)Regions (dynamic size)
Max Pause TimeTarget-based (~200ms)Sub-millisecond
ThroughputHighMedium (due to load barriers)
Best ForGeneral apps, large heapsLow-latency, huge heaps (>32GB)

IV. Concrete Diagnostic Scenario: The "Slow Leak"

Problem: Application throughput degrades over 48 hours. jstat shows the Old Generation is steadily climbing despite frequent GCs.

Step 1: Monitor Allocation vs. Reclamation

# Watch GC stats every 1s
jstat -gcutil <pid> 1000

If O (Old Gen %) increases after every Full GC, you have a Memory Leak (retained references).

Step 2: Sample for Hotspots

Using jcmd to identify which classes are hogging memory:

# Print top 20 classes by memory usage
jcmd <pid> GC.class_histogram | head -n 20

Step 3: Deep Analysis

If a custom class (e.g., com.app.SessionCache) appears at the top, capture a heap dump for the Eclipse Memory Analyzer (MAT):

jcmd <pid> GC.heap_dump /tmp/dump.hprof

V. Critical Tuning Parameters

  1. -Xms / -Xmx: Always set these equal in production to prevent resizing pauses.
  2. -XX:+UseContainerSupport: Mandatory for Docker to ensure the JVM respects cgroup limits.
  3. -XX:MaxDirectMemorySize: Controls off-heap allocation (used by Netty/NIO). If not set, it defaults to -Xmx, which can lead to OS-level OOMs.

See Also: