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.
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.MethodHandleresults
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.loadClassdirectly 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
mainor using
new). During this phase a
java.lang.Classobject 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
<JAVA_HOME>\lib\or the path specified by
-Dbootclasspath(e.g.,
rt.jar,
tools.jar).
Extension Class Loader loads classes from
<JAVA_HOME>\lib\ext\or directories defined by
-Djava.ext.dirs.
System (Application) Class Loader loads classes from the classpath defined by
CLASSPATHor
-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.loadClassmethod 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
Classreference 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
.classfiles 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/classesand
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.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.