Fundamentals 16 min read

When Does Java Load a Class? Exploring the 7 Stages, Parent Delegation, and Custom ClassLoaders

This article explains the complete Java class loading lifecycle—including loading, verification, preparation, resolution, initialization, usage, and unloading—details the seven scenarios that trigger active loading, illustrates the parent delegation model, and provides code examples for custom and Tomcat class loaders.

Ops Development Stories
Ops Development Stories
Ops Development Stories
When Does Java Load a Class? Exploring the 7 Stages, Parent Delegation, and Custom ClassLoaders

Timing of Class Loading

A class's lifecycle in the JVM spans from when it is loaded into memory until it is unloaded, passing through seven stages: loading, verification, preparation, resolution, initialization, usage, and unloading . Verification, preparation, and resolution together constitute the linking phase.

7 Situations When a Class Is Actively Loaded

Creating an instance of the class, e.g.,

new Object()

;

Accessing or assigning a static variable of a class or interface;

Invoking a static method of the class;

Using reflection, such as

Class.forName("com.test.Test")

;

Initializing a subclass of the class;

The class marked as the entry point (containing

main

) when the JVM starts;

Dynamic language support introduced in JDK 1.7, e.g.,

java.lang.invoke.MethodHandle

results

REF_getStatic

,

REF_putStatic

.

When a REF_invokeStatic handle refers to a class that has not been initialized, the class will be initialized.

Other Loading Situations

When the JVM initializes a class, all its superclasses must be initialized; this rule does not apply to interfaces.

Initializing a class does not automatically initialize the interfaces it implements.

Initializing an interface does not automatically initialize its super‑interfaces.

An interface is only initialized when its static variable is first used.

Only when a program actually accesses a static variable or method defined in the current class or interface is it considered an active use.

Calling

ClassLoader.loadClass

directly does not count as active use and will not trigger initialization.

Test Example 1

<code>public class Test_2 extends Test_2_A {
    static {
        System.out.println("子类静态代码块");
    }
    {
        System.out.println("子类代码块");
    }
    public Test_2() {
        System.out.println("子类构造方法");
    }
    public static void main(String[] args) {
        new Test_2();
    }
}

class Test_2_A {
    static {
        System.out.println("父类静态代码块");
    }
    {
        System.out.println("父类代码块");
    }
    public Test_2_A() {
        System.out.println("父类构造方法");
    }
    public static void find() {
        System.out.println("静态方法");
    }
}
// Execution order:
// 1) 父类静态代码块
// 2) 子类静态代码块
// 3) 父类代码块
// 4) 父类构造方法
// 5) 子类代码块
// 6) 子类构造方法</code>

Test Example 2

<code>public class Test_1 {
    public static void main(String[] args) {
        System.out.println(Test_1_B.str);
    }
}

class Test_1_A {
    public static String str = "A str";
    static {
        System.out.println("A Static Block");
    }
}

class Test_1_B extends Test_1_A {
    static {
        System.out.println("B Static Block");
    }
}
// Output:
// A Static Block
// A str</code>

Class Loading Process

Loading

The JVM reads the bytecode file from disk via I/O only when the class is first used (e.g., invoking

main

or using

new

). During this phase a

java.lang.Class

object is created in the method area.

Verification

The bytecode file is checked for correctness.

Preparation

Memory is allocated for static variables and they are given default values.

Resolution

Symbolic references are replaced with direct references (static linking). Dynamic linking, which resolves symbolic references at runtime, occurs later.

Initialization

Static variables are assigned their explicit values and static blocks are executed.

Class Loaders

Bootstrap Class Loader loads core JDK classes from

&lt;JAVA_HOME&gt;\lib\

or the path specified by

-Dbootclasspath

(e.g.,

rt.jar

,

tools.jar

).

Extension Class Loader loads classes from

&lt;JAVA_HOME&gt;\lib\ext\

or directories defined by

-Djava.ext.dirs

.

System (Application) Class Loader loads classes from the classpath defined by

CLASSPATH

or

-Djava.class.path

.

Custom Class Loader loads classes from user‑defined locations by extending

ClassLoader

.

Parent Delegation Model

What Is the Parent Delegation Model?

When a class loader receives a load request, it first delegates the request to its parent. Only if the parent cannot find the class does the child attempt to load it itself. This ensures that core Java classes are loaded by the bootstrap loader, preserving type safety.

The

ClassLoader.loadClass

method follows this delegation logic, first checking if the class has already been loaded, then delegating to the parent, and finally attempting to find the class itself.

<code>protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    synchronized (getClassLoadingLock(name)) {
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // parent could not find the class
            }
            if (c == null) {
                long t1 = System.nanoTime();
                c = findClass(name);
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}
</code>

Summary

The class loader hierarchy is a containment relationship, not a pure tree.

Loading order: bootstrap → extension → system (application) class loader.

The loader that successfully defines a class is called the defining class loader; all loaders that can return a

Class

reference are initial class loaders.

Purpose of the Parent Delegation Model

Ensures type safety of core Java libraries by loading them only through the bootstrap loader, preventing multiple incompatible versions of classes like

java.lang.Object

.

Prevents user‑defined classes from overriding core library classes.

Allows different class loaders to create separate namespaces, enabling multiple versions of the same class to coexist in the JVM.

Custom Class Loader

A simple demo of a custom class loader that reads

.class

files as byte arrays and defines classes.

<code>import java.io.*;

public class ClassLoaderTest extends ClassLoader {
    private static String rxRootPath;
    static { rxRootPath = "/temp/class/"; }
    @Override
    public Class<?> findClass(String name) {
        byte[] b = loadClassData(name);
        return defineClass(name, b, 0, b.length);
    }
    private byte[] loadClassData(String name) {
        try {
            String filePath = fullClassName2FilePath(name);
            InputStream is = new FileInputStream(new File(filePath));
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] buf = new byte[2048];
            int r;
            while ((r = is.read(buf)) != -1) {
                bos.write(buf, 0, r);
            }
            return bos.toByteArray();
        } catch (Throwable e) { e.printStackTrace(); }
        return null;
    }
    private String fullClassName2FilePath(String name) {
        return rxRootPath + name.replace(".", "//") + ".class";
    }
    public static void main(String[] args) throws ClassNotFoundException {
        ClassLoaderTest classLoader = new ClassLoaderTest();
        String className = "com.test.TestAA";
        Class<?> clazz = classLoader.loadClass(className);
        System.out.println(clazz.getClassLoader());
    }
}
</code>

Tomcat Class Loader

Class Loader Model in Tomcat

Explanation of Tomcat Class Loaders

commonLoader : The basic Tomcat loader; classes it loads are visible to the container and all web applications.

catalinaLoader : Private to the Tomcat container; its classes are not visible to web apps.

sharedLoader : Shared among all web apps; its classes are visible to every web app but not to the container itself.

webappLoader : Private to each web application; loads classes from the web app's

WEB-INF/classes

and

WEB-INF/lib

, providing isolation between different web apps.

Summary

From the delegation diagram we see that commonLoader can load classes used by both catalinaLoader and sharedLoader , achieving shared libraries. catalinaLoader and sharedLoader are isolated from each other. Each webappLoader can use classes loaded by sharedLoader but remains isolated from other web apps. The JasperLoader loads only the compiled JSP class and is discarded when the JSP is recompiled, enabling hot reload of JSPs.

Does Tomcat’s class loading violate the parent delegation model? Yes, Tomcat breaks the model to achieve isolation: each webapp loader loads its own classes without delegating to the parent, thus deviating from the standard parent‑delegation mechanism.

JavaJVMClassLoaderTomcatCustom ClassLoaderClass LoadingParent Delegation
Ops Development Stories
Written by

Ops Development Stories

Maintained by a like‑minded team, covering both operations and development. Topics span Linux ops, DevOps toolchain, Kubernetes containerization, monitoring, log collection, network security, and Python or Go development. Team members: Qiao Ke, wanger, Dong Ge, Su Xin, Hua Zai, Zheng Ge, Teacher Xia.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.