Guide
How does class loading work in the JVM?
How the JVM finds, loads, links and initializes classes: the built-in class loaders, parent delegation and the steps defined in chapter 5 of the JVM Specification.
Class loader subsystem
JVM specificationFinds class bytes, turns them into run-time classes, links them and runs their static initialization, on demand.
Simply put
Before Java can use a class, it has to find the class's compiled bytes and turn them into something it can run. This subsystem does that work lazily, the first time a class is actually needed: it loads the class, links it (checks and prepares it) and initializes it (runs its static setup code).
What a developer sees
Classes are loaded on demand. java -cp out app.Main makes the application class loader look for app/Main.class in out; the first new Plugin() or Class.forName("app.Plugin") loads Plugin later. Three built-in loaders work together: bootstrap (core java.* classes), platform and application. For a class on the class path, the application loader first asks the platform loader, which asks the bootstrap loader; a class in a named module goes straight to the loader of that module.
Trouble shows up as ClassNotFoundException (a class looked up by name was not found) or NoClassDefFoundError (typically, a class that was there at compile time is missing at run time). java -Xlog:class+load -cp out app.Main lists every class as it is loaded.
Picture it
A library request desk. You ask for a book by its exact title; the librarian first checks with the main collection, finds the copy, checks it is complete, and only then hands it over.
- The exact title
The class’s full name, such as
app.Main- Checking with the main collection first
Parent delegation for class-path classes: application → platform → bootstrap
- Checking the copy is complete
Verification, part of linking
- Handing it over
The class is loaded and linked; initialization (running its static setup) is a separate step, at its first active use
Where the picture stops working
The JVM keeps the class once it is loaded instead of handing out copies, and two different loaders can each load a separate class with the same name, which a library does not do. Loading also happens only when code first needs the class.
Deep dive: the specification and HotSpot in detail
JVMS Chapter 5 defines three steps. creates a class from a binary representation using a class loader. combines the class into the run-time state: , and, possibly later, . executes the class initialization method <clinit>.
A run-time class is identified by its binary name and its defining class loader, so the same bytes loaded by two loaders produce two distinct classes. HotSpot records loaded classes per loader (ClassLoaderData, SystemDictionary) and stores their metadata in Metaspace.
public class Lazy {
static class Helper {
static {
System.out.println("Helper initialized");
}
static void hello() {
System.out.println("hello");
}
}
public static void main(String[] args) throws Exception {
System.out.println("main started");
Class<?> c = Class.forName("Lazy$Helper", false, Lazy.class.getClassLoader());
System.out.println("loaded: " + c.getName());
Helper.hello();
}
}Verified with OpenJDK 25.0.4 · Linux x64
Common misconceptions
All classes are loaded when the program starts.
Classes are loaded lazily, typically when a symbolic reference to them is first resolved. A trivial program on JDK 25 still loads about 430 classes, almost all of them JDK classes from the CDS archive.
Loading a class runs its static initializer.
Initialization is a separate, later step with precise triggers (JLS §12.4.1). The example above loads
Helperwithout initializing it.
Sources
- SpecJVMS §5.3 Creation and Loading(opens in a new tab)
Defining and initiating loaders; loading with the bootstrap and user-defined loaders. · Java SE 25
- SpecJVMS §5.4 Linking(opens in a new tab)
Verification, preparation and (optionally lazy) resolution. · Java SE 25
- SpecJVMS §5.5 Initialization(opens in a new tab)
Executing <clinit>; the conditions that trigger initialization and its locking procedure. · Java SE 25
- SpecJLS §12.4.1 When Initialization Occurs(opens in a new tab)
The exact events that trigger class or interface initialization. · Java SE 25
- SourcesystemDictionary.cpp(opens in a new tab)
HotSpot's registry of loaded classes per class loader. · JDK 25 GA (tag jdk-25-ga)
Parent delegation
JVM specificationA loader asks its parent first; built-in loaders since JDK 9 first route by module.
Simply put
When a class loader is asked for a class, it first asks its parent loader. Only if the parent cannot provide the class does it look for the class itself. Because the request travels up first, core classes like java.lang.String always come from the JDK and cannot be replaced by a file on your class path.
Deep dive: the specification and HotSpot in detail
The classic algorithm in ClassLoader.loadClass: check findLoadedClass, delegate to the parent, then call findClass. The JDK's built-in loaders refine it (BuiltinClassLoader.loadClassOrNull, JDK 9+): they first map the class's package to a module. If the package belongs to a named module defined to a built-in loader, the request goes directly to that loader, which need not be the parent. Only packages in no named module are delegated to the parent and then searched on the class path.
User-defined loaders may override loadClass (for example child-first web-application loaders). That is legal, but the JVM still enforces loading constraints so a type name cannot mean two different classes across a method call.
System.out.println(String.class.getClassLoader());
System.out.println(java.sql.Connection.class.getClassLoader());
System.out.println(Loaders.class.getClassLoader());
System.out.println(Loaders.class.getClassLoader().getParent());
System.out.println(ClassLoader.getPlatformClassLoader().getName());
System.out.println(ClassLoader.getSystemClassLoader().getName());Verified with OpenJDK 25.0.4 · Linux x64 (hash codes vary between runs)
Common misconceptions
Delegation is always strictly parent-first.
Since JDK 9 the built-in loaders route by module first, and custom loaders may choose child-first. Parent-first is the default for packages that are not in a named module.
The bootstrap class loader is a `ClassLoader` object.
It is built into the JVM. Java APIs represent it as
null, which is whyString.class.getClassLoader()printsnull.
Sources
- APIjava.lang.ClassLoader(opens in a new tab)
Built-in loaders, the delegation model, and getPlatformClassLoader / getSystemClassLoader. · Java SE 25
- SourceBuiltinClassLoader.java(opens in a new tab)
loadClassOrNull: module-aware lookup, then parent delegation, then class path. · JDK 25 GA (tag jdk-25-ga)
- JEPJEP 261: Module System(opens in a new tab)
Platform class loader, module-aware delegation, and the module path. · JDK 9
- SpecJVMS §5.3.4 Loading Constraints(opens in a new tab)
Constraints that keep type safety when classes are visible through different loaders. · Java SE 25
Linking
JVM specificationPrepares a loaded class for execution: verification, preparation and (lazy) resolution.
Simply put
Linking gets a loaded class ready to run. The JVM checks that its bytecode is safe, reserves space for its static fields, and connects names in the code to the real classes, fields and methods.
Deep dive: the specification and HotSpot in detail
JVMS §5.4: linking a class involves and of the class, its direct superclass and superinterfaces, and of symbolic references. The specification lets an implementation resolve eagerly or lazily, provided errors are thrown at the point in the program where the reference is used. HotSpot links a class before it is initialized and resolves references lazily, the first time each instruction executes. Since JDK 24 classes can be loaded and linked ahead of time into an AOT cache (JEP 483).
Common misconceptions
All references in a class are resolved when the class is linked.
HotSpot resolves each symbolic reference the first time an instruction uses it. Code paths that never run never resolve their references.
Sources
- SpecJVMS §5.4 Linking(opens in a new tab)
Verification, preparation and (optionally lazy) resolution. · Java SE 25
- JEPJEP 483: Ahead-of-Time Class Loading & Linking(opens in a new tab)
Classes can be loaded and linked ahead of time into an AOT cache. · JDK 24
Initialization
JVM specificationRuns the class initialization method <clinit>: static initializers in textual order.
Simply put
Initialization runs a class's static setup code: static blocks and static field initializers, top to bottom. It happens once per class, the first time the class is really used.
Deep dive: the specification and HotSpot in detail
JVMS §5.5 and JLS §12.4. A class is initialized on: new, getstatic/putstatic/invokestatic of a member it declares (constant variables excepted), certain reflective calls, initialization of a subclass (the superclass first), and designation as the initial (main) class. The JVM runs the method under a per-class initialization lock; other threads wait until it finishes. If <clinit> throws, the class enters an erroneous state: the first failure surfaces as ExceptionInInitializerError, later uses as NoClassDefFoundError: Could not initialize class ….
$ java -Xlog:class+init=info -cp . PrepVerified with OpenJDK 25.0.4 · Linux x64
Common misconceptions
Static initializers run when the class is loaded.
They run at initialization, which can be much later than loading, or never.
`<clinit>` runs once per thread.
Once per class per class loader, for the life of that class. Other threads block until it completes.
Sources
- SpecJVMS §5.5 Initialization(opens in a new tab)
Executing <clinit>; the conditions that trigger initialization and its locking procedure. · Java SE 25
- SpecJLS §12.4.1 When Initialization Occurs(opens in a new tab)
The exact events that trigger class or interface initialization. · Java SE 25
- SpecJVMS §2.9 Special Methods(opens in a new tab)
Instance initialization methods <init> and class initialization methods <clinit>. · Java SE 25