Backend Development 14 min read

Understanding Java's Parent Delegation Model: Mechanism, Implementation, and How to Break It

This article explains Java's parent delegation class‑loading model, describes the four built‑in class loaders, details why delegation is needed, shows the loadClass implementation, and discusses how and why the mechanism can be overridden in frameworks such as JDBC, Tomcat, OSGi, and the Java 9 module system.

Full-Stack Internet Architecture
Full-Stack Internet Architecture
Full-Stack Internet Architecture
Understanding Java's Parent Delegation Model: Mechanism, Implementation, and How to Break It

During Java interview preparation, the author presents a comprehensive guide to the parent delegation model used by the JVM to load classes.

The JVM defines four class loaders: Bootstrap ClassLoader, Extension ClassLoader, Application ClassLoader, and User‑defined ClassLoader, arranged in a hierarchical parent‑child relationship.

The parent delegation mechanism works as follows: when a class loader receives a request to load a class, it first delegates the request to its parent; only if the parent cannot find the class does the current loader attempt to load it.

Reasons for using delegation include preventing duplicate class loading and ensuring core Java classes (e.g., those in rt.jar ) cannot be replaced by malicious code, thereby enhancing security.

In the ClassLoader source, the parent reference is defined as:

public abstract class ClassLoader {
    // The parent class loader for delegation
    private final ClassLoader parent;
}

The core of the delegation logic resides in ClassLoader.loadClass(String name, boolean resolve) :

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;
    }
}

The loading steps are:

Check if the class has already been loaded.

If not, delegate to the parent loader.

If the parent is null, use the bootstrap loader.

If delegation fails, invoke findClass to load the class.

To break the delegation, one can create a custom class loader that overrides loadClass and skips the parent call, or override findClass after JDK 1.2, which is the recommended approach.

Typical examples of breaking delegation include:

JNDI/JDBC loading SPI implementations via Thread.currentThread().getContextClassLoader() :

public static
ServiceLoader
load(Class
service) {
    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    return ServiceLoader.load(service, cl);
}

Here the context class loader (usually AppClassLoader ) loads third‑party drivers, bypassing the bootstrap loader.

Tomcat also breaks delegation by providing a separate WebAppClassLoader for each web application, allowing different versions of the same library to coexist.

Modular systems such as OSGi and Java 9 further modify the model. Java 9 replaces the monolithic rt.jar with many modules, and the class‑loading algorithm first looks for a module‑specific loader before falling back to parent delegation:

Class
c = findLoadedClass(cn);
if (c == null) {
    LoadedModule loadedModule = findLoadedModule(cn);
    if (loadedModule != null) {
        BuiltinClassLoader loader = loadedModule.loader();
        c = findClassInModuleOrNull(loadedModule, cn);
    } else {
        if (parent != null) {
            c = parent.loadClassOrNull(cn);
        }
    }
}

In summary, the article covers what parent delegation is, why it exists, how it is implemented in the JVM, and multiple real‑world scenarios where the model is intentionally broken to achieve isolation, extensibility, or modularity.

ClassLoaderCustom ClassLoaderModule SystemClass LoadingParent Delegation
Full-Stack Internet Architecture
Written by

Full-Stack Internet Architecture

Introducing full-stack Internet architecture technologies centered on Java

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.