How Java ClassLoaders Work

Advanced
7 min read· Backend & Databases

A ClassLoader is the part of the JVM that finds and loads .class files into memory on demand. Rather than one loader, the JVM uses a hierarchy — bootstrap, platform, and application loaders — connected by parent-first delegation, which protects core classes from being replaced. Custom class loaders power plugin systems, application servers, and hot reloading, but they are also the source of a notorious kind of memory leak.

Think of class loaders as a chain of librarians

You ask the junior librarian (application loader) for a book. Before searching, they ask their senior (platform loader), who asks the head librarian (bootstrap loader). Only if the seniors do not have it does the junior look on their own shelves. This parent-first order means a core reference book (java.lang.String) always comes from the head librarian and can never be swapped for a counterfeit copy on a junior shelf.

Step by Step

1 / 5

Key Concepts

Parent Delegation

Each loader first asks its parent to load a class, only loading it itself if no ancestor can. This prevents application code from shadowing or spoofing core Java classes.

ClassNotFoundException vs NoClassDefFoundError

ClassNotFoundException is thrown when explicit loading (Class.forName) cannot find a class. NoClassDefFoundError occurs when a class was present at compile time but is missing at runtime.

Classloader Leak

When a long-lived object (a static field, a running thread) references a class from a redeployed app loader, the whole loader — and every class it loaded — cannot be garbage collected, leaking Metaspace.

Class Identity

A loaded class is uniquely keyed by name plus loader. The same class file loaded by two loaders produces incompatible types, causing ClassCastException between them.

Key Facts

  • Application servers give each deployed app its own loader so their libraries do not clash — and so an app can be redeployed without restarting the JVM.
  • The classic "OutOfMemoryError: Metaspace" after repeated redeploys is almost always a classloader leak.
  • GraalVM Native Image trades this flexibility away: it fixes the set of classes at build time, so there is no dynamic class loading at runtime.

Real-World Applications

Plugin architectures

Tools that load third-party plugins give each plugin its own class loader so plugins can use different library versions and be unloaded cleanly by dropping the loader.

Hot reload in development

Frameworks reload changed classes by creating a fresh loader for the updated code and discarding the old one — letting you see edits without restarting the server.

Frequently Asked Questions

What is the parent delegation model?

When asked to load a class, a loader first delegates to its parent, which delegates to its parent, up to the bootstrap loader. Only if no ancestor can load the class does the original loader try. This guarantees core classes always come from the trusted bootstrap loader.

Why do I get NoClassDefFoundError but not ClassNotFoundException?

NoClassDefFoundError means the class existed when you compiled but is missing (or failed to initialise) at runtime — often a missing dependency jar or a static initialiser that threw. ClassNotFoundException means an explicit lookup like Class.forName could not find the class at all.

What causes a classloader memory leak?

A reference from outside the app (a static field in a JDK or shared library, a ThreadLocal, a running thread) pointing to a class from the redeployed app keeps that app class loader alive, so none of its classes can be unloaded — exhausting Metaspace over repeated redeploys.

Can two classes with the same name coexist?

Yes — if loaded by different class loaders they are distinct types. This is how app servers isolate deployments, but mixing instances across loaders causes ClassCastException even though the class names match.

Related Topics