Class Loading Process & Parent Delegation
Java class loading follows Load → Link (verify, prepare, resolve) → Initialize. Parent delegation means a class loader first delegates to its parent; only if the parent cannot load the class does the child attempt to load it. This ensures core classes (e.g. java.lang.*) are loaded by Bootstrap/System loaders and prevents applications from overriding JDK classes. This article explains the phases and delegation with examples and a reference table.
Overview
- Phases: Load (read binary) → Verify (format, bytecode) → Prepare (static default values) → Resolve (symbolic refs → direct refs, can be deferred) → Initialize (run <clinit>, static blocks and assignments).
- Parent delegation: When
ClassLoader.loadClassis called, it first delegates to the parent. If the parent loads the class, that result is returned. Otherwise the child callsfindClass. Thusjava.lang.*is loaded by Bootstrap/Ext/App; application classes are loaded by App or custom loaders. - Breaking delegation: For SPI (e.g. JDBC Driver), the interface (
DriverManager) is in rt.jar (Bootstrap), but implementations are on the classpath (App). The thread context class loader lets Bootstrap code obtain the App loader to load implementations. This is a deliberate "break" of parent-first delegation.
Example
Example 1: Delegation chain
Plain textCustom ClassLoader → AppClassLoader → ExtClassLoader → BootstrapClassLoader
- To load
com.example.Foo: Custom delegates to App, App to Ext, Ext to Bootstrap. Bootstrap does not know it, so control returns: Ext → App → Custom. Custom'sfindClassreads the class file and callsdefineClass.
Example 2: Why String cannot be replaced
java.lang.Stringis loaded by Bootstrap. If the application could load its own "fake" String first, type safety would break. Parent delegation ensures "if the parent can load it, the child does not"; core classes stay with the top-level loader and cannot be replaced by application code.
Example 3: Context class loader (SPI)
JavaServiceLoader<Driver> loader = ServiceLoader.load(Driver.class); // Internally uses Thread.currentThread().getContextClassLoader() // to load implementations from META-INF/services
- SPI interfaces are in rt.jar (Bootstrap); implementations are on the classpath (App). Bootstrap code uses the context class loader to "reverse delegate" to App and load the implementation classes.
Example 4: Custom ClassLoader (findClass only)
Javapublic class MyClassLoader extends ClassLoader { private final Path basePath; @Override protected Class<?> findClass(String name) throws ClassNotFoundException { byte[] bytes = loadClassBytes(name); return defineClass(name, bytes, 0, bytes.length); } }
- Override only
findClass; leaveloadClassas-is to preserve parent delegation.
Example 5: Phase summary
| Phase | Content |
|---|---|
| Load | Read class binary, create Class object |
| Verify | Format, bytecode, symbolic references |
| Prepare | Static fields get default values (0, null) |
| Resolve | Symbolic refs → direct refs (can be deferred) |
| Initialize | Run <clinit> (static blocks, assignments). Triggered by: new, static access, reflection, subclass init |
Core Mechanism / Behavior
- Delegation: In
loadClass, callparent.loadClassfirst, thenfindClass. Custom loaders typically override onlyfindClass, notloadClass, to keep delegation. - Namespace: The same class name loaded by different ClassLoaders yields different classes (
equalsis false). Tomcat uses separate ClassLoaders per webapp for isolation. - Unloading: A class can be unloaded when it has no references and its ClassLoader is GC-eligible. Application ClassLoaders usually live for the process, so classes rarely unload.
Key Rules
- Override only findClass in custom ClassLoaders; keep the default
loadClassdelegation to avoid breaking parent delegation. - Understand SPI and the context class loader: when the interface is in Bootstrap and the implementation is on the classpath, use the context class loader to load the implementation.
- For
ClassNotFoundExceptionorNoClassDefFoundError, check classpath, dependencies, ClassLoader hierarchy, and module/package visibility.
What's Next
See JVM Memory Model for method area/Metaspace. See Reflection for class loading triggers.