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, worldvoid main() { IO.println("hello"); }
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.
what actually happens when you run IO.println("hello"). every artifact below is real output from jdk 25.
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, worldvoid main() { IO.println("hello"); }
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.
final class Main
└── method main()
└── call IO.println
└── literal "hello"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: bytecodevoid main(); Code: 0: ldc #7 // String hello 2: invokestatic #9 // Method java/lang/IO.println 5: return
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.
00000000: cafe babe 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
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.
[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
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→ ldc "hello" invokestatic IO.println return
ldc pushes a reference to the "hello" string. invokestatic pops it and calls println.
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 JITms 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)
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.)
; 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.
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 works18 parts, 192 chapters. read it in order, or jump to whatever you're stuck on. your progress stays in your browser.
what java even is, a JDK on your machine, and your first program running.
variables, types, control flow, methods, and the stack/heap mental model everything else sits on.
classes, constructors, inheritance, polymorphism, interfaces, and the contracts every object signs.
the standard library you use every day, and how errors move through a program.
type parameters, wildcards, erasure. how java gets type safety without changing the JVM.
lists, sets, maps, queues. what each one actually is in memory, and which to pick.
functions as values, and pipelines that process data lazily, one element at a time.
records, sealed types, pattern matching. java from 8 to 27 and how it writes now.
threads, races, locks, the memory model, executors and virtual threads.
bytes in, bytes out. files, buffers, sockets, http, databases and crypto.
code that looks at code. reflection, annotations, proxies, method handles, invokedynamic.
the JDK's own tools, modules, build tools, testing, and the libraries everyone uses.
class files, bytecode, class loaders, the interpreter and the JIT. how your code actually runs.
allocation, reachability, and every collector from Serial to generational ZGC.
measuring honestly, profiling, flame graphs, the CPU underneath, and calling C.
a companion to Bloch's 90 items: the idea, the bad version, the good version, and what changed since.
the interview-grade internals. source-level, bit-level, no hand-waving.
valhalla, leyden, amber, loom, panama. what's landing next and why it matters.
what every page here promises.