Fundamentals 17 min read

Unraveling Java’s ClassLoader: From Parent Delegation to Custom Loading

This article explores Java's class loading mechanism in depth, covering when and how classes are loaded, the parent‑delegation model, the internal role of ClassFileParser and InstanceKlass, parallel loading nuances, and practical ways to break or extend the delegation chain with custom class loaders.

Alibaba Cloud Developer
Alibaba Cloud Developer
Alibaba Cloud Developer
Unraveling Java’s ClassLoader: From Parent Delegation to Custom Loading

1. Introduction

The author was asked to write a deep dive on the JVM ClassLoader and uses this opportunity to explain the parent‑delegation model and the overall class loading process.

2. When a Java class is loaded

According to "Deep Understanding of the Java Virtual Machine", a class is loaded when:

new, getstatic, putstatic instructions are executed;

reflection is used;

a subclass is initialized;

the JVM starts and loads the main class;

dynamic language support in JDK 1.7 is invoked.

In short, a class is loaded the moment the runtime needs it.

3. How a class is loaded

Loading is performed by calling ClassLoader.loadClass(). Example:

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        Test.class.getClassLoader().loadClass("com.wangxiandeng.test.Dog");
    }
}

The call eventually reaches the native method ClassLoader.defineClass1(), which invokes Java_java_lang_ClassLoader_defineClass1 in the HotSpot source.

This native method calls JVM_DefineClassWithSource(), which leads to jvm_define_class_common() in jvm.cpp. The method creates a ClassFileStream, resolves the class, and builds an internal representation called Klass. Klass has two concrete subclasses: InstanceKlass (for regular Java classes) and InstanceMirrorKlass. InstanceKlass stores annotations, constant pool, fields, methods, inner classes, etc., essentially the in‑memory form of a .class file.

3.1 resolve_from_stream()

The core of class loading is resolve_from_stream(), which performs three steps:

Determine whether parallel loading is allowed and acquire the appropriate lock.

Parse the class file stream to create an InstanceKlass via KlassFactory::create_from_stream().

Register the resulting Klass in the SystemDictionary.

If parallel loading is enabled, only SystemDictionary is locked; otherwise the ClassLoader itself is locked to guarantee a single InstanceKlass per loader.

3.2 ClassFileParser

The ClassFileParser is the real workhorse. Its create_instance_klass() method allocates memory for an InstanceKlass (using the overloaded Klass::operator new that allocates in Metaspace) and then fills the structure with data from the class file.

InstanceKlass* ik = new (loader_data, size, THREAD) InstanceKlass(parser, InstanceKlass::_misc_kind_other);
fill_instance_klass(ik, changed_by_loadhook, CHECK_NULL);
java_lang_Class::create_mirror(ik, Handle(THREAD, loader_data->class_loader()), module_handle, _protection_domain, CHECK);

After this, the class file has been transformed into a live InstanceKlass object.

4. Breaking the parent‑delegation model

To demonstrate isolation, a custom class loader is introduced:

public class CustomClassloader extends URLClassLoader {
    public CustomClassloader(URL[] urls) { super(urls); }
    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        if (name.startsWith("com.wangxiandeng")) {
            return findClass(name);
        }
        return super.loadClass(name, resolve);
    }
}

Loading Student with this loader and casting it to the system‑loaded Student results in a ClassCastException because the two loaders create distinct InstanceKlass objects.

Exception in thread "main" java.lang.ClassCastException:
    com.wangxiandeng.Student cannot be cast to com.wangxiandeng.Student

The article explains that the checkcast bytecode resolves the constant‑pool entry, which triggers loading of Student.class by the system loader, producing a different InstanceKlass from the one created by CustomClassloader.

4.1 How the JVM resolves classes

When the constant‑pool entry is unresolved, the interpreter calls InterpreterRuntime::quicken_io_cc, which loads the class via the current loader chain. To insert a custom loader into this chain, two approaches are shown:

Set the system class loader via the JVM argument -Djava.system.class.loader=YourLoader.

Replace the thread context class loader with Thread.setContextClassLoader(customLoader) and load classes reflectively.

URL[] url = new URL[1];
url[0] = Thread.currentThread().getContextClassLoader().getResource("");
CustomClassloader custom = new CustomClassloader(url);
Thread.currentThread().setContextClassLoader(custom);
Class clazz = custom.loadClass("com.wangxiandeng.ClassTest");
Method m = clazz.getDeclaredMethod("test");
m.invoke(clazz.newInstance());

This avoids the premature loading of ClassTest by the default loader and ensures the custom loader participates in constant‑pool resolution.

5. Summary

Understanding the JVM's class loading pipeline—from the parent‑delegation rule, through resolve_from_stream(), ClassFileParser, and InstanceKlass creation—to the ways of customizing or breaking the delegation chain, is essential for advanced Java development and for building class‑loader‑based isolation mechanisms.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaJVMclassloaderCustomClassLoaderParent DelegationInstanceKlass
Alibaba Cloud Developer
Written by

Alibaba Cloud Developer

Alibaba's official tech channel, featuring all of its technology innovations.

0 followers
Reader feedback

How this landed with the community

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.