JVM Concepts

How the JVM works under the hood, one concept at a time.

Sort
1

Sizing the JVM Heap: RAM/CPU Economics in Practice

Why trading RAM for CPU is usually the economically correct call given how cloud and consumer hardware price RAM per core, how to measure real allocation rate and GC CPU cost instead of guessing, why naive cross-language memory benchmarks mislead, and what adaptive heap sizing is proposing to automate next.

2

Moving Garbage Collectors and the RAM/CPU Trade-off

Why the JVM's moving, generational collectors never actually free an object the way malloc/free or reference counting do, and how that design turns GC's CPU cost into a direct function of heap headroom you control with -Xmx.

3

Bytecode Instructions: Branching, Returns, and Synchronization

How conditional branches always compile to the inverse of the source condition, why == on objects is if_acmpeq (identity, never equals()), why a switch compiles to tableswitch or lookupswitch depending on label density, why return is six type-specific instructions instead of one, and why a synchronized block costs a compiler-generated exception table that a synchronized method never needs.

4

Bytecode Instructions: Object Creation, Field Access, and Method Dispatch

Why 'new' is allocate-then-construct as two separate instructions, how field and array access split into distinct opcode families, and why the JVM has five different invoke* instructions instead of one — including why modern javac no longer emits invokespecial for private method calls.

5

Bytecode Instructions: The Operand Stack, Arithmetic, and Type Conversions

How bytecode's stack-machine arithmetic works — the i/l/f/d mnemonic prefixes, why float and double comparisons need two opcodes to handle NaN correctly, and why narrowing conversions like d2i and i2b lose data silently instead of throwing.

6

ZGC Concurrent Collection: Colored Pointers and Allocation Stalls

How ZGC's colored pointers and load barrier keep pauses sub-millisecond by moving objects concurrently, what generational ZGC changed in JDK 21-24, and why an allocation stall — not a pause — is ZGC's actual failure mode to monitor.

7

G1 Internals: Regions, Humongous Objects, and Evacuation Failure

How G1 lays out the heap as regions instead of fixed generations, why a 1MB+ allocation can skip eden and land straight in old, and how to read a GC log line well enough to tell a healthy young collection from an evacuation failure.

8

Collections API and Streams: Sizing, Synchronization, and Laziness

Why collections are unsynchronized by default, why sizing a collection up front avoids real resize costs, and why a chain of Stream operations processes far less data than it looks like it does.

9

NIO Servers, Async Responses, and Virtual Threads

Why Java servers moved from one-thread-per-connection to NIO selectors and async response patterns, and how virtual threads have since made most of that ceremony unnecessary to write by hand.

10

Native Memory and JVM Footprint: Reserved vs. Committed

How the JVM's total footprint (heap plus native memory) works, why reserved memory isn't the same as memory actually in use, and how to break it down with Native Memory Tracking.

11

JDBC Statement Pooling and JPA's N+1 Problem

Why prepared statements only pay off when pooled per connection, and why JPA's eager relationship fetching issues one query per related entity instead of a single JOIN — the classic N+1 query problem.

12

String Performance: Compact Strings, Deduplication, and Concatenation

How compact strings roughly halve the heap cost of an average String, when G1 string deduplication is worth turning on, and why the compiler's own concatenation optimization usually beats a hand-rolled StringBuilder chain.

13

Microbenchmarking Pitfalls and JMH

Why a hand-rolled "time a loop" benchmark routinely measures nothing (dead-code elimination, missing warm-up, constant folding), and how JMH's Blackhole exists specifically to close those holes.

14

Java Flight Recorder: Low-Overhead Production Profiling

How JFR's event-based, sub-1%-overhead recording lets you profile a live production JVM continuously, and how to control it entirely from the command line.

15

Thread Pool Sizing: CPU-Bound vs. I/O-Bound Workloads

Why sizing a ThreadPoolExecutor for compute-bound work means matching the core count, sizing for I/O-bound work means far exceeding it, and which half of this problem virtual threads actually solved.

16

Heap Analysis and Reading OutOfMemoryError Correctly

How to use heap histograms and heap dumps to find out what's consuming memory, and how to read the different flavors of OutOfMemoryError instead of just adding more heap.

17

Choosing a Garbage Collector: Serial, Parallel, G1, ZGC, and Shenandoah

How the JVM's generational collectors trade off pause time, throughput, and CPU overhead, and which one the JVM actually defaults to today versus what a 2020-era book assumes.

18

JIT Compilation and Tiered Compilation: C1, C2, and Warm-Up

How the JVM compiles bytecode to native code while the program runs, why it waits for code to get "hot" before compiling it, and how tiered compilation combines a fast-starting compiler (C1) with a slower, smarter one (C2).

19
🔬 Lab

The .class File Format: Header, Constant Pool, Fields, and Methods

How javac turns Java source into a structured binary .class file — magic number and version in the header, the constant pool's table of symbolic references, access flags, how fields and methods are laid out for the JVM to load and execute, and the one-letter descriptor codes (B/C/D/F/I/J/S/Z, L...;, [) that encode every type in the file.