Introduction to Java ClassLoader

Java programming language is known for its range of features, providing extensive control to developers. One such feature is the Java ClassLoader, which is part of the Java Runtime Environment that lets developers load Java classes dynamically during runtime to the JVM (Java Virtual Machine).

Thanks to ClassLoader, the JVM does not need to know about the underlying files or file systems to run Java programs. All these Java classes are not directly loaded into memory all together but only when it is required by an application. This is the primary task of ClassLoader for class loading in Java only when needed.

In this article, we will be discussing different types of built-in Java ClassLoaders, how they work and how to use them.

Types of Java ClassLoaders

Before starting with the different types of Java Classloaders, let’s take a look at the code snippet below showing how different ClassLoaders are used for class loading in Java.

1. public void showClassLoaders() throws ClassNotFoundException {
2. System.out.println("this is the Classloader of this class:" + showClassLoader.class.getClassLoader());
3. System.out.println("Classloader of a class called ArrayList:" + ArrayList.class.getClassLoader());
4. System.out.println("Classloader of a class called Logging:" + Logging.class.getClassLoader());
5. } 

This will be the output of the above code,

this is the Classloader of this class: sun.misc.Launcher$AppClassLoader@18b4aac2
Class loader of a class called ArrayList:null
Class loader of a class called Logging:sun.misc.Launcher$ExtClassLoader@3caeaf62

The example above simply shows three different ClassLoaders application, bootstrap and extension.

1. Bootstrap ClassLoader

An instance of Java.lang.ClassLoader is used to load Java classes. The interesting question is if Java ClassLoader itself is also a class then how is the Java.lang.ClassLoader leaded? The answer is: through Bootstrap ClassLoader.

A Bootstrap ClassLoader is not a Java class, it is a part of the core JVM, written in native code as shown in the above example. It starts the operation when it is called by the JVM. It is responsible to load the first pure Java ClassLoader along with the JDK internal classes, which are in rt.jar format and other core libraries located in $JAVA_HOME/jre/lib directory.

Bootstrap ClassLoader does not have any parent ClassLoader. It serves as a parent of all the other ClassLoader instances.

2. Extension ClassLoader

The Extension ClassLoader is a child of the Bootstrap ClassLoader and it is responsible for the loading of the extensions of the standard core Java classes. It must be available to all applications running on the platform. Extension ClassLoader loads from the JDK extensions directory, $JAVA_HOME/lib/ext directory or it can be loaded from another directory mentioned in the Java.ext.dirs system property.

3. System ClassLoader

The System ClassLoader is a child of the Extensions ClassLoader. It is responsible for loading all the application level classes into the JVM.  System ClassLoader loads the files present in the classpath environment variable, -classpath or -cp command line option. 

How does Java ClassLoader work?

As ClassLoader is part of the Java Runtime Environment. Whenever JVM requests a class, the ClassLoader attempts to locate and load its class definition into the runtime using the fully qualified class name.

The Java.lang.ClassLoader.loadClass() method is primarily used for loading the class definition during runtime. In case, if the class is not already loaded, it requests to the parent ClassLoader and this process takes place recursively. In the end, if the parent ClassLoader cannot find the class, then the child class will call Java.net.URLClassLoader.findClass() method to look for classes in the file system by itself.

In the worst-case scenario, even If the last child ClassLoader could not load the class, it throws an exception “Java.lang.NoClassDefFoundError or Java.lang.ClassNotFoundException.

Here is the example of output showing if the ClassNotFoundException is thrown.

Java.lang.ClassNotFoundException: com.baeldung.classloader.SampleClassLoader   
    at Java.net.URLClassLoader.findClass(URLClassLoader.Java:381)    
    at Java.lang.ClassLoader.loadClass(ClassLoader.Java:424)    
    at Java.lang.ClassLoader.loadClass(ClassLoader.Java:357)    
    at Java.lang.Class.forName0(Native Method)    
    at Java.lang.Class.forName(Class.Java:348)

3 sets of rules for every Java ClassLoader

This was the basic walkthrough of a Java ClassLoader in all possible scenarios. Now, the following are the three extremely important functionality rules followed by every Java ClassLoader.

1. Delegation Model

 The Java Virtual Machine and the Java ClassLoader use a specific algorithm to load the classes into the Java file. It is called Delegation Hierarchy Algorithm.

The Java ClassLoader always works upon the operations provided by the delegation model and follows the delegation hierarchy algorithm.

The algorithm is as follows:

  1. When JVM comes across a request to find a Java class or resource, it first checks whether that class is already loaded or not.
  2. If the class is already loaded in the method area, then the JVM simply proceeds with the execution.
  3. In case if the class is not loaded in the method area then the JVM always first delegates the system Java ClassLoader to load that particular class. The system ClassLoader then delegates its control to the Application ClassLoader.
  4. Application ClassLoader then again delegates the request further down the hierarchy to the Extension ClassLoader and then lastly, Extension ClassLoader delegates the request to Bootstrap ClassLoader.
  5. Bootstrap ClassLoader would now search in the Bootstrap classpath (JDK/JRE/LIB) for that class. If the class is available, then it will load it. Otherwise, the request will be delegated to Extension ClassLoader.
  6. Extension ClassLoader looks for the class in the Extension Classpath (JDK/JRE/LIB/EXT). If the class is available then it is loaded, otherwise, it is delegated to the Application ClassLoader.
  7. If the application ClassLoader is unsuccessful in loading the class, then ultimately a ClassNotFoundException exception is thrown.

2. Unique Classes Rule

Before loading the classes, it is first ensured that the classes are unique and there are no repetitions. It is also made sure that the classes loaded by the parent ClassLoader must not be loaded by the child ClassLoader. If the parent ClassLoader could not find the class, only then the current instance would attempt to do so itself.

3. Visibility Rule

Children ClassLoaders are always visible to the classes loaded by their parent ClassLoaders. The classes loaded by the system ClassLoader have visibility into classes loaded by the extension and Bootstrap ClassLoaders but it is not the case vice-versa.

For instance, if a Class 1 is loaded by an application, ClassLoader and Class 2 is loaded by the extensions ClassLoader, then both Class 1 and 2 are visible. Class 2 will be the only class visible as far as other classes loaded by the extension ClassLoader are concerned.

What is a custom Java ClassLoader?

Built-in Java ClassLoader is sufficient in most cases when the files are already present in the file system. However, in some scenarios where we need to load classes from a local hard drive or a network, then built-in class loaders will not be enough. You will require a custom Java ClassLoader to perform such tasks.

A custom Java ClassLoader can do way more than just load the class during runtime, it can be used for modifying an existing bytecode or for creating a class dynamically as per the user’s requirements. You will also require a custom ClassLoader for class versioning while loading different bytecodes for classes with the same names and packages.

Its most common example can be found in browsers. A custom ClassLoader is used in browsers to load executable content from a site. A browser loads applet from different web pages using different ClassLoaders. The applet viewer contains a ClassLoader that accesses the website from a remote server instead of the local file system.

How to create a custom ClassLoader?

Creating a custom Java ClassLoader is not a complicated task. You just need to extend the ClassLoader class and override the finalClass() method.

In the following example, we have defined a custom ClassLoader that extends the ClassLoader class and loads a byte array from a specified file.

1.	public class CustomClassLoader extends ClassLoader {
2.	    @Override
3.	    public Class findClass(String className) throws ClassNotFoundException {
4.	        byte[] a = loadClassFromFile(className);
5.	        return defineClass(className, a, 0, a.length);
6.	    }
7.	    private byte[] loadClassFromFile(String fileName) {
8.	        InputStream inputStream = getClass().getClassLoader().getResourceAsStream(
9.	                fileName.replace('.', File.separatorChar) + ".class");
10.	        byte[] buffer;
11.	        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
12.	        int nextValue = 0;
13.	        try {
14.	            while ( (nextValue = inputStream.read()) != -1 ) {
15.	                byteStream.write(nextValue);
16.	            }
17.	        } catch (IOException e) {
18.	            e.printStackTrace();
19.	        }
20.	        buffer = byteStream.toByteArray();
21.	        return buffer;
22.	    }
23.	}

Methods of Java.lang.ClassLoader

Java.lang.ClassLoader class features some very useful methods that make class loading possible. We will further discuss a few important methods from the Java.lang.ClassLoader class and how they work.

1. loadClass() Method

public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { … }

The loadclass() method loads the class by the given name parameter. The name parameter is the fully qualified name of the class you want to load.

JVM invokes the loadClass() method to resolve class references setting and to determine if the class exists or not. This method acts like an entry point for the class loader.

You can see the loadClass() method code below for a better understanding of the internal working of this method:

1.	protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException     
2.	synchronized (getClassLoadingLock(className)) {
3.	//First,it will check in case if the class has already been loaded.      
4.	  Class<?> c = findLoadedClass(className);
5.	     if (c == null) {
6.	       long t0 = System.nanoTime();
7.	         try {
8.	             if (parent != null) {
9.	               c = parent.loadClass(className, false);
10.	              } else {
11.	                  c = findBootstrapClassOrNull(className);
12.	                  }
13.	               } catch (ClassNotFoundException e) {                
14.	// If the class is not found a ClassNotFoundException will be thrown from the non-null parent class loader
15.	                  }
16.	 
17.	              if (c == null) {
18.	// even if the class is not found, then it will invoke findClass method in order to find the class.
19.	                 c = findClass(className);
20.	                }
21.	            }
22.	            if (resolve) {
23.	                resolveClass(c);
24.	            }
25.	        return c;
26.	     }
27.	}

Default order is followed to load the class:

First, the findLoadedClass() method is invoked to check if the class is already loaded.

Then the loadClass() method is invoked on the parent class loader.

If the class is still not found then the findClass() method is invoked to find the class.

2. defineClass() Method

protected final Class<?> defineClass(
  String name, byte[] b, int off, int len) throws ClassFormatError { … }

The defineClass() method converts an array of bytes into an instance of a class. It is also checked if the converted data did not contain a valid class, it will throw a “ClassFormatError” error.

It is also important to consider that this method cannot be overridden as it is marked as final.

3. findClass() Method

protected Class<?> findClass(
  String name) throws ClassNotFoundException {…}

As its name suggests, findclass() method finds the class with the fully qualified name as a parameter. We need to override this method in custom classloader implementations that follow the delegation model for class loading in Java.

loadClass() method invokes this method as a last resort if the parent class loader could not find the requested class.

4. getParent() Method

public final ClassLoader getParent()
getParent() returns the parent class loader for delegation.

Some implementations of getParent() use null to represent the bootstrap class loader.

5. getResource() Method

public URL getResource(String name)

This method tries to find a resource with the name passed as a parameter. Initially, it will delegate to the parent ClassLoader for the resource but if the parent is null, the path of the ClassLoader built-in JVM is then searched for the resource.

If that fails as well, then the getResource() method will invoke another method called findResource() to find the resource. If resources are found a URL object will be returned by the findResource() method for reading the resource, or null will be returned if the resource could not be found or if the invoker does not have the required privileges.

Conclusion

Java ClassLoader is one of the very powerful features in Java. We have covered all the basic details about Java ClassLoader in the discussion above. You will also find the working of  Java ClassLoader, how it is used, the basic classification of ClassLoaders, some important methods in Java.lang.ClassLoader class and how you can create a custom Java loader.

See Also: How to Write a Java Agent

This article could be a good starting point for you if you want to start using Java ClassLoader.

Author

Shaharyar Lalani is a developer with a strong interest in business analysis, project management, and UX design. He writes and teaches extensively on themes current in the world of web and app development, especially in Java technology.

Write A Comment