Exploring Java Class Loaders

An introduction to various class loaders in Java, this post is part 1 in the "Java Class Loaders" series. In the next post, we'll explore the different phases of class loading process, like loading, linking, and initialization.

Class Loaders play a pivotal role in dynamically loading Java classes into the JVM (Java Virtual Machine) during application execution. Instead of loading all Java classes into memory at once, class loaders fetch classes only when they are requested by the application, which reduces the startup time.

The ClassLoader fetches binary data from the .class files (from local or remote locations) and loads it into the JVM method area. For a given class, this class loading process occurs just once, during the initial reference of a class within the active Java application. Subsequent references reuse the data stored in the method area unless the class has been UNLOADED.

Class Loaders

Quoting JVM specification, two types of class loaders are available in Java:

  1. Bootstrap: native code.
  2. User-Defined: java classes - Platform and Application class loader.

When the JVM starts, these class loaders are set to action:

1. Bootstrap

Written in native code (thus may have different implementations on different platforms), it loads core Java classes from $JAVA_HOME/jre/lib location.

The bootstrap class loader is also known as the primordial class. As various instances of java.lang.ClassLoader (which are classes themselves) are responsible for loading all other classes, an obvious question that comes to mind is who loads the type java.lang.ClassLoader itself? And the answer is the bootstrap class loader.

The Bootstrap class loader is the parent of all the other ClassLoader instances. Before Java9, it was responsible for loading classes from rt.jar, but with the introduction of the module system in Java9, each module is now responsible for defining its own class loader. The bootstrap class loader is now responsible for loading the module system itself - via the java.base module.

2. Extension or Platform Class Loader

A child of the bootstrap class loader, it loads the extensions of the standard core Java classes. It loads classes JAVA_HOME/lib/ext directory or any other directory defined in the java.ext.dirs system property.

Instead of the extension class loader, Java9, and later versions use the java.lang.ModuleLayer class to load modules from the extension directory. The extension directory is now treated as a separate layer in the module system, and the extension layer’s class loader loads modules in the extension directory.

Also, note that in Java9 and later versions, it is recommended to use modules instead of the extension mechanism to share code between applications. The extension mechanism is still available for backward compatibility with older applications but is not recommended for new code.

3. System or Application Class Loader

The system or application class loader loads all the application-level classes into the JVM defined via the -classpath or -cp command line option.

How Class Loaders Work?

When a class is requested, the class loader locates and loads the class definition into the runtime using the fully qualified class name. The java.lang.ClassLoader#loadClass() method loads the class definition into runtime.

It is done by delegating the request to the parent class loader if the class isn’t already loaded. This scan is done recursively up the class loader hierarchy. Eventually, if none of the parent class loaders can find the class, the java.net.URLClassLoader#findClass() is invoked to scan the file system.

At last, if the complete class loader hierarchy can’t load the class, an appropriate contextual exception java.lang.NoClassDefFoundError or java.lang.ClassNotFoundException is thrown.

Java Class Loaders Hierarchy

Delegation Model: Java Class loaders follow the delegation model, where the request to load a class or resource is recursively passed to the parent class loader. Only if the parent class loader(s) are unsuccessful in loading the class does the child class loader try to load the class itself.

Unique Classes: The delegation model mentioned above helps to ensure that only unique classes are loaded to the runtime in a single class loader context. Classes loaded by different class loaders (say custom ones) are treated differently, even if they have the same fully qualified class name.

Visibility: Allows child class loader to see all the classes loaded by parent class loader, but parent class loader cannot see classes loaded by child.

Java9 Changes

There are some significant changes to the class loading process in Java9. As mentioned in the migration guide:

  1. ExtensionClassLoader was renamed to PlatformClassLoader to provide support for the module system. All classes in the Java SE Platform are guaranteed to be visible through the platform class loader and can be used as the parent of a ClassLoader instance.

  2. Application class loader loads the application modules found on the module path and some JDK modules like jdk.compiler, jdk.javadoc, jdk.jshell, etc.

  3. Before JDK9, the extension and the application class loader were an instance of the java.net.URLClassLoader class. In JDK9, the renamed platform and application class loaders are an instance of an internal JDK class.

  4. Removed Extension Mechanism: In JDK 9, if you need to use the extension classes, ensure that the JAR files are on the class path.

  5. In JDK 9, ClassLoader.getSystemResource doesn’t return a URL pointing to a JAR file (rt.jar and tools.jar were removed). Instead, it returns a jrt URL, which names the modules, classes, and resources stored in a runtime image without revealing the internal structure or format of the image.

    Java8: jar:file:/usr/local/jdk8/jre/lib/rt.jar!/java/lang/Class.class
    Java9: jrt:/java.base/java/lang/Class.class
    
  6. The bootstrap class loader is still built-in to the Java Virtual Machine and represented by null in the ClassLoader API. It defines the classes in a handful of critical modules, such as java.base.

  7. A single class loader is responsible for loading a specific module, but a class loader may be responsible for an arbitrary number of modules. The built-in modules are all loaded by either of Bootstrap or Platform class loaders. The application-specific modules specified at startup are all loaded by the System class loader, also known as the Application class loader. These three belonging to the boot layer

When you create a new module layer, you can demand whether a distinct class loader or a single one loads each module.

Modules and Class Loaders

Class loading being dynamic, calculating the absolute number of classes inside a module is a bit challenging, but the packages are fixed and can be easily queried as follows:

class Scratch{
    public static void main(String[] args){
        ModuleLayer layer = ModuleLayer.boot();
        layer.modules().stream()
            .filter(module -> module.getName().equals("java.base"))
            .forEach(module -> {
                ClassLoader classLoader = module.getClassLoader();
                String classLoaderName = isNull(classLoader) ? "bootstrap" : classLoader.getName();
                System.out.println("classLoader: " + classLoaderName + "; module: " + module.getName() + "; packages: " + module.getPackages());
        });
    }
}

// classLoader: bootstrap
// module: java.base
// packages: [sun.security.rsa, java.lang.reflect, sun.net.util, java.nio, java.nio.file.spi, java.util.jar, sun.net.www.protocol.http, jdk.internal.perf, javax.security.auth, com.sun.crypto.provider, jdk.internal.foreign, sun.text.resources.cldr, jdk.internal.io, sun.security.timestamp, sun.util.resources.cldr, java.security.cert, jdk.internal.foreign.abi.x64, jdk.internal.util.jar, jdk.internal.util, jdk.internal.platform, sun.security.internal.spec, sun.launcher, jdk.internal.logger, jdk.internal.org.objectweb.asm.commons, jdk.internal.icu.text, jdk.internal.org.objectweb.asm.signature, sun.net.www.protocol.jrt, java.io, jdk.internal.foreign.abi.x64.sysv, sun.security.provider, sun.security.tools, sun.reflect.generics.repository, sun.util.cldr, jdk.internal.vm, java.nio.channels, sun.util.calendar, java.util.concurrent.locks, jdk.internal.foreign.abi, jdk.internal.module, javax.security.auth.login, java.lang, javax.net.ssl, sun.net.ftp, javax.crypto.spec, sun.security.provider.certpath.ssl, sun.util.spi, jdk.internal.org.objectweb.asm.util, sun.reflect.generics.tree, sun.reflect.generics.parser, jdk.internal.util.regex, jdk.internal.util.xml.impl, sun.security.util, sun.text, sun.security.jca, sun.reflect.annotation, sun.security.x509, jdk.internal.math, java.nio.charset, java.text.spi, java.util, sun.net.www.protocol.http.ntlm, sun.util.locale.provider, java.security.spec, jdk.internal.vm.vector, jdk.internal.access, java.util.spi, sun.security.action, jdk.internal.org.xml.sax.helpers, jdk.internal.foreign.abi.aarch64.macos, java.lang.foreign, apple.security, javax.net, sun.net.dns, java.lang.ref, javax.crypto, java.util.concurrent.atomic, sun.security.provider.certpath, sun.net.www.protocol.jmod, sun.text.resources, sun.launcher.resources, jdk.internal.org.objectweb.asm, jdk.internal.jimage.decompressor, sun.security.util.math, java.util.zip, jdk.internal.event, sun.net.www.protocol.file, jdk.internal, jdk.internal.jrtfs, jdk.internal.util.xml, jdk.internal.foreign.layout, java.lang.annotation, java.math, sun.text.spi, javax.crypto.interfaces, java.lang.module, java.time, jdk.internal.icu.impl, sun.reflect.generics.visitor, sun.security.tools.keytool, javax.security.auth.callback, java.time.chrono, jdk.internal.org.objectweb.asm.tree.analysis, jdk.internal.icu.lang, com.sun.security.ntlm, jdk.internal.javac, jdk.internal.misc, sun.reflect.generics.scope, java.util.random, sun.net.sdp, jdk.internal.org.xml.sax, sun.invoke.util, sun.invoke.empty, sun.net.spi, sun.net, java.time.temporal, jdk.internal.loader, jdk.internal.vm.annotation, java.nio.channels.spi, jdk.internal.foreign.abi.aarch64, jdk.internal.icu.util, jdk.internal.jmod, sun.net.www.protocol.ftp, jdk.internal.ref, sun.util.logging, java.nio.file.attribute, sun.nio.ch, sun.net.www.protocol.https, sun.net.www.protocol.jar, sun.reflect.generics.reflectiveObjects, java.time.format, javax.security.auth.x500, sun.nio.cs, sun.security.ssl, java.net, javax.security.cert, jdk.internal.reflect, sun.invoke, sun.security.pkcs10, java.net.spi, sun.security.pkcs12, sun.util, sun.net.ftp.impl, sun.reflect.generics.factory, jdk.internal.org.objectweb.asm.tree, java.time.zone, sun.net.smtp, sun.net.www.protocol.mailto, java.text, jdk.internal.icu.impl.data.icudt72b, sun.security.internal.interfaces, java.util.stream, java.util.regex, jdk.internal.foreign.abi.x64.windows, sun.net.idn, javax.security.auth.spi, sun.util.locale, sun.reflect.misc, java.security, java.security.interfaces, jdk.internal.jimage, sun.net.www, java.lang.constant, sun.security.validator, java.nio.file, jdk.internal.util.random, sun.net.www.content.text, sun.nio, sun.net.ext, java.lang.invoke, java.lang.runtime, java.nio.charset.spi, jdk.internal.foreign.abi.aarch64.linux, sun.security.util.math.intpoly, sun.nio.fs, sun.net.www.http, sun.security.pkcs, sun.util.resources, jdk.internal.access.foreign, java.util.concurrent, java.util.function]

Context Classloaders

In multithreaded environments, it is common for different threads to have additional class loading requirements. The ContextClassLoader is a mechanism introduced to address this requirement.

The context class loader is associated with a thread and can be set and retrieved using the Thread class’s setContextClassLoader and getContextClassLoader methods. It allows a thread to specify a class loader that will be used for loading classes when needed, overriding the default delegation model.

This is particularly useful in scenarios like application servers and frameworks, where different components may have their own class loading requirements.


That is all for this post. If you want to share any feedback, please drop me an email, or contact me on any social platforms. I’ll try to respond at the earliest. Also, please consider subscribing feed for regular updates.

References

  1. ClassLoader JavaDocs
  2. Java9 ClassLoader changes
  3. ClassLoaders and Modules

Be notified of new posts. Subscribe to the RSS feed.