2026-10-31·by Sijie Wang

Class Loading

Class loading: load on demand, delegate to parents, break delegation to hot-swap

The motivation. Java loads classes at runtime, on demand: compiled .class bytecode becomes in-memory classes only when used. That buys dynamism (plugins, hot updates, SPI) and lazy loading (startup does not load everything).

Three steps. Loading: read the .class byte stream, build the data structure in the method area, and create a Class object on the heap. Linking: verification (is the bytecode legal), preparation (static fields get default values), resolution (symbolic references become direct references, which may be deferred). Initialization: run , static blocks and static assignments in order, superclasses before subclasses. Only active references trigger initialization: new, accessing a static field or method, reflection, subclass initialization, and the main class.

Loader hierarchy and parent delegation. Top-down: the Bootstrap loader (implemented in C++, loads core classes), the Platform loader, the Application loader (classpath), then custom loaders. Parent delegation means: ask the parent loader first, and only look yourself if the parent cannot find it. Two benefits: core classes can never be replaced by a custom class of the same name (security), and the same class is never loaded twice.

Breaking delegation is the hot-update principle. A class's identity is its fully qualified name plus its loader: the same name under different loaders is a different class. So a hot update loads each version with a fresh loader, and drops the old loader; the old classes become unloadable once three conditions hold: all instances of the class are garbage collected, the Class object is unreachable, and the loader itself is unreachable. A custom classloader hot-update design rests on exactly this.

Two neighbours that are easy to confuse with class loading.

Graal: two identities, two different kinds of "hot". As a JIT compiler, Graal plugs into HotSpot through JVMCI (JVM Compiler Interface) and replaces C2. Its "hot" is runtime code replacement: profile, speculatively optimize (assuming a type or a branch), and on a broken assumption deoptimize back to interpretation or recompile. What changes is compiled code, not classes. As Native Image, Graal is the opposite direction: ahead-of-time compilation with a closed world, where dynamic class loading at runtime is restricted and reflection and resources need manifests. So "Graal can hot-load" is false for Native Image, and true but not about classes for the Graal JIT.

Lombok: a compile-time trick, not a runtime one. Annotations such as @Getter, @Setter, @Data, @Builder, @Slf4j save boilerplate. Standard annotation processing (JSR 269) can only generate new files, not modify existing ones, so Lombok goes further: it uses javac's internal APIs to rewrite the AST (abstract syntax tree) in memory after parsing and before bytecode generation, injecting methods and fields. That is why generated methods are invisible in source but present in the compiled class, and why Lombok is fragile across JDK versions: it depends on JDK internal APIs, and strong module encapsulation since JDK 16 (JEP 396/403) repeatedly forces adaptations, often needing --add-opens. It is still mainstream (notably @Builder and @Slf4j, which have no JDK equivalent), though simple data carriers have partly moved to records (JDK 16+).

about this entry

One of sijie's wiki entries. The AI on this site is grounded in the same corpus and answers in sijie's voice, with citations back to entries like this one — answering costs sijie money, so it waits behind a code: enter an access code →

Class Loading — Sijie Wang