Cheat SheetsJava A–ZJVM Internals & Memory

JVM Internals & Memory — Cheat Sheet

Java A–Z · 5 topics. Download the PDF or the Instagram carousel and share it.

Cheat Sheet · AiCanCode.org
JVM Internals & Memory
Java A–Z5 topicsQuick revision reference
1

JVM Architecture

  • Class loading: Loading → Linking (Verify/Prepare/Resolve) → Initialisation.
  • Bootstrap → Platform → Application class loaders form the delegation chain.
  • Heap: shared object storage. Stack: per-thread method frames. Metaspace: class metadata (off-heap).
  • JIT compiles hot methods to native code; Tiered Compilation uses C1 then C2.
  • StackOverflowError = stack full; OutOfMemoryError = heap or Metaspace full.
ClassLoaders.java
// Inspect class loaders
Class<?> cls = String.class;
System.out.println(cls.getClassLoader()); // null = Bootstrap

Class<?> userCls = MyApp.class;
ClassLoader appLoader = userCls.getClassLoader();
System.out.println(appLoader);             // AppClassLoader
System.out.println(appLoader.getParent()); // PlatformClassLoader
System.out.println(appLoader.getParent().getParent()); // null (Bootstrap)

// Force class loading
Class<?> loaded = Class.forName("com.example.SomeService");
// Triggers: Loading → Linking → Initialisation

// Lazy loading — class loaded only when first used
// JVM loads a class the first time its bytecode is needed

// Custom class loader
public class HotReloadLoader extends ClassLoader {
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] bytes = loadBytecodeFromDisk(name);
        return defineClass(name, bytes, 0, bytes.length);
    }
}
2

Garbage Collection

  • Generational hypothesis: most objects die young — GC exploits this with Eden/Survivor/Old.
  • Minor GC collects Young Gen (fast); Full GC collects everything (slow — minimize it).
  • G1GC: region-based, meets pause time target, default since Java 9.
  • ZGC: sub-millisecond pauses, concurrent relocation, ideal for latency-critical large heaps.
  • Common memory leaks: unclosed resources, static collections, ThreadLocal without remove(), inner class references.
GCOverview.java
/*
  Object lifecycle in generational GC:

  new Object()
       │
       ▼
  ┌─────────┐  Minor GC  ┌──────┐  N GCs  ┌──────────┐
  │  Eden   │ ─────────► │ S0/S1│ ───────► │ Old Gen  │
  └─────────┘            └──────┘          └──────────┘
    Most objects           Survivors          Long-lived
    die here               bounce S0↔S1       objects
    (very fast GC)         (age threshold)    (expensive GC)
*/

// GC tuning flags
// -Xms512m        — initial heap size
// -Xmx2g          — maximum heap size
// -Xmn512m        — Young Gen size
// -XX:+UseG1GC    — use G1 collector (default Java 9+)
// -XX:+UseZGC     — use ZGC (Java 15+, sub-ms pauses)
// -XX:MaxGCPauseMillis=200  — G1 pause time target
// -XX:+PrintGCDetails       — verbose GC logging
// -Xlog:gc*:file=gc.log     — GC log to file (Java 9+)
3

JVM Tuning and Profiling

  • Set -Xms equal to -Xmx to avoid heap resize pauses in production.
  • Always enable -XX:+HeapDumpOnOutOfMemoryError and -XX:+ExitOnOutOfMemoryError.
  • jcmd is the preferred tool for live diagnostics — heap dumps, thread dumps, JFR control.
  • JFR has ~1% overhead — safe for continuous production profiling.
  • Allocation pressure and lock contention are the two most common Java performance bottlenecks.
JvmFlags.sh
# Production JVM flags template

# Heap sizing
-Xms4g -Xmx4g          # fixed heap — avoids resizing pauses
-XX:+UseG1GC            # G1GC (default Java 9+)
-XX:MaxGCPauseMillis=100 # target max pause

# GC logging (Java 9+ unified logging)
-Xlog:gc*:file=/logs/gc.log:time,level,tags:filecount=5,filesize=20m

# Diagnostics
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/dumps/heap-$(date +%s).hprof
-XX:+ExitOnOutOfMemoryError   # restart rather than limp on

# JFR continuous profiling (low overhead ~1%)
-XX:StartFlightRecording=filename=recording.jfr,settings=profile,duration=60s

# Metaspace
-XX:MetaspaceSize=256m        # initial Metaspace commit size
-XX:MaxMetaspaceSize=512m     # cap to prevent unbounded growth
4

Java Memory Model (JMM)

  • JMM defines happens-before: if A HB B, all effects of A are visible when B executes.
  • Volatile write HB subsequent volatile read — ensures visibility without locking.
  • Synchronized unlock HB subsequent lock of the same monitor.
  • final fields are safely published after constructor completes — no synchronisation needed.
  • Safe publication: use volatile, final, synchronized, or concurrent collections to share objects.
VisibilityBug.java
// Classic visibility bug (may loop forever)
class BrokenStop {
    static boolean stop = false; // no volatile

    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            while (!stop) {} // JIT may hoist 'stop' read out of the loop
            System.out.println("stopped");
        }).start();

        Thread.sleep(100);
        stop = true; // write may never be flushed to main memory
                     // or the reader thread may never see it
    }
}

// Why this can fail:
// 1. JIT compiler can cache 'stop' in a register (loop invariant hoisting)
// 2. CPU write buffer may not flush to main memory
// 3. Reader CPU cache may not invalidate its cached copy

// Fix: declare stop as volatile — establishes happens-before
5

Class Loading and Hot Reload

  • Class identity = fully-qualified name + ClassLoader — same name, different loaders = different types.
  • Parent delegation prevents classpath classes from overriding Bootstrap classes (java.lang.*).
  • Hot reload: break parent delegation for target classes, create a new loader, discard the old.
  • Class unloading: happens when loader is GC'd — requires no live references to loader or its classes.
  • Metaspace leaks: typically caused by class loader leaks (dynamic proxies, runtime code generation).
LoaderIdentity.java
// Two loaders — two identities
ClassLoader loader1 = new URLClassLoader(urls);
ClassLoader loader2 = new URLClassLoader(urls);

Class<?> cls1 = loader1.loadClass("com.example.Plugin");
Class<?> cls2 = loader2.loadClass("com.example.Plugin");

System.out.println(cls1 == cls2); // FALSE — different identities!

// ClassCastException across loaders
Object obj = cls1.getDeclaredConstructor().newInstance();
// com.example.Plugin cast = (com.example.Plugin) obj;
// ^ ClassCastException if Plugin was loaded by the app loader
//   but obj was loaded by loader1

// Check which loader loaded a class
System.out.println(String.class.getClassLoader());         // null (Bootstrap)
System.out.println(ArrayList.class.getClassLoader());      // null (Bootstrap)
System.out.println(MyService.class.getClassLoader());      // AppClassLoader
Learn this free with Aria, your AI tutor → AiCanCode.org/learn/java