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.
Quoting JVM specification, two types of class loaders are available in Java:
When the JVM starts, these class loaders are set to action:
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.
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.
The system or application class loader loads all the application-level classes into the JVM defined via the -classpath
or -cp
command line option.
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.
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.
There are some significant changes to the class loading process in Java9. As mentioned in the migration guide:
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.
Application class loader loads the application modules found on the module path and some JDK modules like jdk.compiler
, jdk.javadoc
, jdk.jshell
, etc.
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.
Removed Extension Mechanism: In JDK 9, if you need to use the extension classes, ensure that the JAR files are on the class path.
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
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
.
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.
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]
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.
Be notified of new posts. Subscribe to the RSS feed.