Skip to content

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 specification

Finds 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.

Loading is not initializationJava
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 Helper without initializing it.

Sources

Parent delegation

JVM specification

A 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.

Who defined these classes?Java
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 why String.class.getClassLoader() prints null.

Sources

Linking

JVM specification

Prepares 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

Initialization

JVM specification

Runs 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 ….

Watch initialization orderShell
$ java -Xlog:class+init=info -cp . Prep

Verified 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

JVM Atlas

Explore the machine behind Java programs. A free, open-source atlas of the Java Virtual Machine. Specification baseline: Java SE 25. Implementation details: OpenJDK HotSpot, JDK 25.

MIT licensed · no accounts, no tracking, no backend · your progress stays in your browser.

JVM Atlas is an independent educational project and is not affiliated with or endorsed by Oracle. Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.