Phase 06 — JVM Internals & Performance (Months 8–9)¶
This is the phase where you stop treating the JVM as a black box. You learn what happens between javac and a stack trace: how classes load, how bytecode is compiled to native code, how garbage collectors free memory without stopping the world (usually), and how to find out — with hard evidence — why a service is slow, allocating like crazy, or falling over with OutOfMemoryError.
Senior engineers get paid for this. Not because it comes up every day, but because when it does come up, the business is on fire and everybody looks at you. The right answer isn’t “restart it and add more heap.” The right answer is a flame graph, a GC log excerpt, and a one-line explanation of what the JVM was actually doing.
You’ll spend roughly 5–6 weeks here. Slower than earlier phases, because the mental model is dense and the tooling requires practice. Do not rush.
Weekly Schedule¶
Week |
Focus |
Deliverable |
|---|---|---|
33 |
JVM architecture, class loading, bytecode reading with |
Notes + one bytecode dissection of a |
34 |
Memory layout, generational GC, choosing a collector |
Read three real GC logs; annotate them |
35 |
JIT tiers, inlining, deopts, GraalVM native-image trade-offs |
|
36 |
|
Record a 60s JFR on your rate limiter; open in JMC |
37 |
JMH — lifecycle, |
Five JMH benchmarks (Project B) |
38 |
Production pathologies: OOM species, long pauses, thread starvation |
Project C: three OOMs, three fixes, three heap dumps |
Files in This Phase¶
File |
What You’ll Learn |
|---|---|
Class loaders, bytecode, tiered compilation, stack vs heap vs Metaspace |
|
Heap regions, generational hypothesis, G1/ZGC/Shenandoah, tuning flags, reading GC logs |
|
Inlining, escape analysis, deoptimization, JIT logs, GraalVM AOT |
|
|
|
Why hand-rolled benchmarks lie; JMH lifecycle, blackholes, interpreting output |
|
OOM species, long GC pauses, thread starvation, connection pool exhaustion, container awareness |
|
Profile a slow service; five JMH benchmarks; deliberately cause and fix three OOMs |
Exit Criteria¶
You’ve cleared this phase when you can honestly tick every box:
You can read a
javap -cbytecode listing for a small method and explain each opcode.You can name every region of a G1 heap and describe what triggers a Young vs Mixed vs Full GC.
You can pick G1, ZGC, or Parallel for a given workload and defend the choice with two numbers.
You’ve captured a 60-second JFR recording and read the resulting flame graph in JMC.
You’ve used Async Profiler for CPU, allocation, and lock profiling on the same app.
You’ve analysed a heap dump in Eclipse MAT and identified a dominator.
You’ve written a JMH benchmark that uses
Blackhole.consume(...)correctly and reported score ± error.You can diagnose a heap OOM, a Metaspace OOM, and a direct-buffer OOM from the error message alone.
You can explain
-XX:MaxRAMPercentageand why it matters in Kubernetes.
⚠️ What Most People Get Wrong¶
Two failure modes to avoid before you write a single line:
1. Cargo-cult GC flags. You will find blog posts from 2014 recommending flags that are removed, renamed, or actively harmful on modern JVMs. -XX:+UseConcMarkSweepGC is gone in Java 14+. -XX:+UseParNewGC is gone. -XX:PermSize and -XX:MaxPermSize were dropped in Java 8 (there’s no PermGen anymore). Rule: flags older than the JVM you’re running are suspect. Consult the JEP that introduced the collector, not a random Medium post.
2. Benchmarks that measure the wrong thing. A five-line main with System.nanoTime() around a loop is not a benchmark — it’s a coin toss. JIT hasn’t warmed up, dead-code elimination erased half your work, and constant-folding did the rest. If you show a colleague timing numbers that were not produced by JMH (or a comparable framework), you should expect to be politely ignored. Learn JMH properly in file 05.
Prerequisites¶
You should have shipped Phase 05 in some form (at least Projects A and B). GC and JIT questions get real once you have a live executor pool to observe.
You also need a JDK 21+ install with jcmd, jstack, jmap, jinfo, jstat, and jfr on your PATH. On macOS with Homebrew: brew install --cask temurin@21. Verify with jcmd -h. Async Profiler is a separate download; install it now: curl -L -o async-profiler.tar.gz https://github.com/async-profiler/async-profiler/releases/latest/download/async-profiler-3.0-macos.tar.gz.
Return to ../README.md · Next: 01_jvm_architecture.md