In this post, we’ll talk about class loaders in Java, how they work, and what the different types of class loaders are.
As Java programmers, it’s important for us to understand how class loaders work because they can affect how our code runs.
Introduction
Class loaders are a fundamental part of the Java Runtime Environment (JRE). They are responsible for loading Java classes into memory at runtime. Thanks to class loaders, the JVM doesn’t need to know about the underlying files or file systems to run Java applications.
A class loader is responsible for loading classes. When passed the binary/fully qualified name of a class, a class loader will try to find or generate data that defines the specified class. Typically the binary name is converted into a file name, and then a “class file” of that name is read from the underlying file system.
As a Java application starts, the classes aren’t all loaded into memory at once. They are loaded by the class loaders as and when they’re needed by the application.
Every Class
object contains a reference to the ClassLoader
that loaded it. We can find this by calling the getClassLoader()
method of the Class
class.
The class ClassLoader
is an abstract class. We can implement our own class loaders to customise how classes are loaded into the JVM.
Types of Class Loaders
There are three main types of class loaders in Java:
- The bootstrap class loader loads the core Java classes that are needed for the JVM to work.
- The extension class loader loads classes that are part of Java extensions.
- The application/system class loader loads classes that are part of the running application.
Let’s look at each of these class loaders in more detail.
Bootstrap Class Loader
Java classes are loaded by an instance of java.lang.ClassLoader
. However, class loaders are also classes. So what loads the java.lang.ClassLoader
?
This is where the bootstrap or primordial class loader comes into the picture. The bootstrap class loader is part of the core JVM. It is written in native code, and its behaviour differs slightly across JVMs.
The bootstrap class loader is the parent of all the other ClassLoader
instances. It’s responsible for loading the core Java classes that the JVM needs. These classes are found in the rt.jar
file and other libraries in the $JAVA_HOME/jre/lib
directory (in Java 8).
Because the bootstrap class loader is written in native code it doesn’t display as a normal Java class. Some JVMs use null
to represent the bootstrap class loader. null
is also used for primitive types and void
.
Extension Class Loader
The extension class loader is a child of the bootstrap class loader. It loads the extensions to the core Java classes from the JDK extensions directory. This is usually the $JAVA_HOME/lib/ext
directory, or any directory specified in the java.ext.dirs
system property. This is for Java 8 and below. Java 11 and later do not have extension directories.
The extension mechanism was removed in Java 9 and later versions. This means that the extension directories are no longer used to load Java extension classes. We now use the module system that was introduced in Java 9 to create and load custom libraries.
Application/System Class Loader
The application or system class loader loads the classes that are part of the application currently being run. It loads classes in the directories and JAR files specified by the CLASSPATH
environment variable or the -classpath
command line option. It’s also a child of the extensions class loader.
Example Code
The following code prints the class loaders for different classes.
import java.sql.DriverManager;
import java.util.ArrayList;
public class PrintClassLoaders {
public static void main(String args[]) {
System.out.println("java.ext.dirs: "
+ System.getProperty("java.ext.dirs"));
System.out.println("Classloader of ArrayList: "
+ ArrayList.class.getClassLoader());
System.out.println("Classloader of String: "
+ String.class.getClassLoader());
System.out.println("Classloader of this class: "
+ PrintClassLoaders.class.getClassLoader());
System.out.println("Classloader of DriverManager: "
+ DriverManager.class.getClassLoader());
System.out.println("Classloader of com.sun.nio.zipfs.ZipInfo: "
+ com.sun.nio.zipfs.ZipInfo.class.getClassLoader());
}
} // end of class
Java 8 Output
Running this in Java 8 will give an output similar to the following (object addresses will differ on your machine):
java.ext.dirs: d:\jdk8\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext
Classloader of String: null
Classloader of ArrayList: null
Classloader of this class: sun.misc.Launcher$AppClassLoader@73d16e93
Classloader of DriverManager: null
Classloader of com.sun.nio.zipfs.ZipInfo: sun.misc.Launcher$ExtClassLoader@70dea4e
We can see that the class loaders for String
, ArrayList
and DriverManager
all return null
. This generally represents the bootstrap class loader.
The PrintClassLoaders
application class returns sun.misc.Launcher$AppClassLoader
. This is the application/system class loader.
The ZipInfo
class loader returns sun.misc.Launcher$ExtClassLoader
. This is the extension class loader. The ZipInfo
class was chosen at random to illustrate this.
Java 11 Output
Running the same code in Java 11 without recompiling will give an output similar to the following:
java.ext.dirs: null
Classloader of ArrayList: null
Classloader of String: null
Classloader of this class: jdk.internal.loader.ClassLoaders$AppClassLoader@30946e09
Classloader of DriverManager: jdk.internal.loader.ClassLoaders$PlatformClassLoader@5cbc508c
Exception in thread "main" java.lang.NoClassDefFoundError: com/sun/nio/zipfs/ZipInfo
at PrintClassLoaders.main(PrintClassLoaders.java:24)
Caused by: java.lang.ClassNotFoundException: com.sun.nio.zipfs.ZipInfo
...
We see that the java.ext.dirs
is null
. Remember that the extension mechanism has been removed in Java 9 and later. That’s why we get a NoClassDefFoundError
for com/sun/nio/zipfs/ZipInfo
. Neither the directory nor the JAR files exist in Java 11. Obviously we could remove the line referring to the ZipInfo
class.
The class loaders for String
and ArrayList
return null
, which is the bootstrap class loader.
The PrintClassLoaders
application class is loaded by the application/system class loader. This class loader is different to Java 8. It is now jdk.internal.loader.ClassLoaders$AppClassLoader
. The AppClassLoader
is responsible for loading classes from the application classpath and module path.
The DriverManager
class loader now returns jdk.internal.loader.ClassLoaders$PlatformClassLoader
. The PlatformClassLoader
is a class loader introduced in Java 9 as part of the module system. It replaces the extension class loader. The PlatformClassLoader
is responsible for loading classes from the Java SE Platform and JDK-specific tools. It is a parent/ancestor of the AppClassLoader
. This mean that all platform classes are visible to it.
Conclusion
Next week we’ll look how class loaders work. We’ll look at creating a custom class loader implementation. We very seldom need to create our own class loaders. But we can implement subclasses of ClassLoader
to customise the way the JVM loads classes.
Was this post useful? Let us know in the comments, and as always, stay safe and keep learning!