skip to content

java, all the way down

a visual guide to java, by harshpreet · harshpreet.com

an open-source book that draws java instead of describing it. 192 chapters, from int x = 5 to the garbage collector. every figure moves, every snippet runs, every number was measured. free, forever.

or scroll, and follow one line of java all the way down.

one line of java, all the way down

what actually happens when you run IO.println("hello"). every artifact below is real output from jdk 25.

depth 0 of 8

your code

three lines. since java 25 you don't even need a class. this is what you write, and it's the only layer most people ever look at.

read: hello, world
Main.java
void main() {
    IO.println("hello");
}
depth 1 of 8

javac

the compiler reads characters, groups them into tokens, builds a tree and checks every type. it also wraps your method in an implicit final class Main, because the JVM only understands classes.

read: compile and run
tokens → tree
voidmain(){IO.println("hello");}
final class Main
└── method main()
    └── call IO.println
        └── literal "hello"
depth 2 of 8

bytecode

javac doesn't make machine code. it makes instructions for an imaginary stack machine: push a constant, call a static method, return. three of them, for your whole program.

read: bytecode
javap -c Main.class
void main();
  Code:
     0: ldc           #7   // String hello
     2: invokestatic  #9   // Method java/lang/IO.println
     5: return
depth 3 of 8

the class file

all of that, packed into 310 bytes. the first four are always CA FE BA BE. the next four say which java it was compiled for: 0x45 is 69, which means java 25.

read: the class file
xxd Main.class
00000000: cafe babe 0000 0045 0016 0a00 0200 0307
00000010: 0004 0c00 0500 0601 0010 6a61 7661 2f6c
00000020: 616e 672f 4f62 6a65 6374 0100 063c 696e
00000030: 6974 3e01 0003 2829 5608 0008 0100 0568
00000040: 656c 6c6f 0a00 0a00 0b07 000c 0c00 0d00
00000050: 0e01 000c 6a61 7661 2f6c 616e 672f 494f
   ...   310 bytes total
cafe babe magic0045 major version 69656c 6c6f "hello"
depth 4 of 8

class loading

java Main starts the JVM, and a class loader finds Main.class, checks it, and turns it into a live class. IO comes from the JDK itself, via a different loader. hello world loads 418 classes before it exits.

read: class loaders
java -Xlog:class+load Main
[0.007s] java.lang.Object source: shared objects file
   ...    400 more JDK classes
[0.017s] Main source: file:/…/descent/
[0.018s] java.lang.IO source: jrt:/java.base
bootstrap loader   java.lang.*  (IO, Object)
  └── platform loader
        └── app loader   Main
depth 5 of 8

the interpreter

the JVM runs the bytecode one instruction at a time, pushing and popping an operand stack. it's slow-ish, and that's fine: most code only ever runs a few times.

read: bytecode, stepped
operand stack
  → ldc "hello"
    invokestatic IO.println
    return
● → "hello"

ldc pushes a reference to the "hello" string. invokestatic pops it and calls println.

depth 6 of 8

the JIT

run something a million times and the JVM notices. it compiles the hot method to native code with C1 (fast to compile), then again with C2 (fast to run). this is real output: tier 3, then tier 4, within the first 15 ms.

read: the interpreter and the JIT
java -XX:+PrintCompilation Main
ms  id   tier
15   9      3  Main::square (4 bytes)
15  10      4  Main::square (4 bytes)
15   9      3  Main::square made not entrant
16  11 %    3  Main::main @ 4 (32 bytes)
16  13 %    4  Main::main @ 4 (32 bytes)
0 interp
1 c1
2 c1
3 c1+prof
4 c2
depth 7 of 8

machine code

and at the bottom, it's just instructions for your CPU. square(x) becomes a multiply and a return. (simplified: real C2 output has a prologue, a safepoint poll and some bookkeeping around it.)

read: JIT optimizations
arm64, simplified
; int square(int x)   x arrives in w1
  mul   w0, w1, w1   ; w0 = x * x
  ret              ; back to the caller

from IO.println("hello") to here is six layers. this site teaches all of them.

depth 8 of 8

and the garbage collector

meanwhile, every object you made sits on the heap, and a collector keeps finding the ones nothing points at anymore. you never call it. it's always there.

read: how garbage collection works
heap
reachablegarbagefree

the whole book

18 parts, 192 chapters. read it in order, or jump to whatever you're stuck on. your progress stays in your browser.

  1. part i6 ch
    start here

    what java even is, a JDK on your machine, and your first program running.

  2. part ii14 ch
    the basics

    variables, types, control flow, methods, and the stack/heap mental model everything else sits on.

  3. part iii13 ch
    objects and classes

    classes, constructors, inheritance, polymorphism, interfaces, and the contracts every object signs.

  4. part iv11 ch
    core apis and exceptions

    the standard library you use every day, and how errors move through a program.

  5. part v7 ch
    generics

    type parameters, wildcards, erasure. how java gets type safety without changing the JVM.

  6. part vi12 ch
    collections

    lists, sets, maps, queues. what each one actually is in memory, and which to pick.

  7. part vii10 ch
    lambdas and streams

    functions as values, and pipelines that process data lazily, one element at a time.

  8. part viii10 ch
    modern java

    records, sealed types, pattern matching. java from 8 to 27 and how it writes now.

  9. part ix16 ch
    concurrency

    threads, races, locks, the memory model, executors and virtual threads.

  10. part x10 ch
    i/o, networking and data

    bytes in, bytes out. files, buffers, sockets, http, databases and crypto.

  11. part xi8 ch
    reflection and metaprogramming

    code that looks at code. reflection, annotations, proxies, method handles, invokedynamic.

  12. part xii10 ch
    tools and ecosystem

    the JDK's own tools, modules, build tools, testing, and the libraries everyone uses.

  13. part xiii12 ch
    inside the JVM

    class files, bytecode, class loaders, the interpreter and the JIT. how your code actually runs.

  14. part xiv13 ch
    memory and garbage collection

    allocation, reachability, and every collector from Serial to generational ZGC.

  15. part xv9 ch
    performance and native

    measuring honestly, profiling, flame graphs, the CPU underneath, and calling C.

  16. part xvi11 ch
    effective java, visualized

    a companion to Bloch's 90 items: the idea, the bad version, the good version, and what changed since.

  17. part xvii14 ch
    deep dives

    the interview-grade internals. source-level, bit-level, no hand-waving.

  18. part xviii6 ch
    where java is going

    valhalla, leyden, amber, loom, panama. what's landing next and why it matters.

the house rules

what every page here promises.

figures
they move. step through them, drag them, break them.
code
real. every snippet runs on jdk 25, most of them right here in your browser.
numbers
measured, and we say on what machine.
depth
from your first variable to the JIT and the garbage collector, in one voice.
price
free. MIT licensed. no account, no ads, no tracking.