Have you ever seen a ClassNotFoundException or a NoClassDefFoundError?
Did you ever face a situation where you are trying to cast and assign it to a compatible reference but it throws a ClassCastException ?
Or did you face issues where you never understood where to put the jars in your application?
Ever did any hot deployment on a running Application Server and wondered how the new changes get reflected without a server restart?
Ever got confused with the process of Class Loading in JVM?
If you didn’t face any of these, then you are extremely lucky or you are yet to start development 🙂 . This post will try to outline and explain the complex process of Class loading. Here I will try to put it in the simplest possible way.
Before I start, we need to get our understanding straight on the the notion of a class. In this post when we talk about a class, we mean the class file which conforms to the byte code specification laid by the Java Language Specification and JVM Specification.
The process of Class Loading in JVM
In the previous post, we mentioned the Class creation and loading phase when a JVM starts. Here we will discuss the details of the loading process. Loading is the process of obtaining the byte array which is the equivalent of a class file. This byte array can be obtained from any of the following:
- A physical file located on the underlying file system
- A blob cell in a database table
- A stream of bytes over a network and many more sources.
Once the byte array is obtained, the class loader must define the class and make an entry of the class to the JVM.
What is a Class Loader?
This is the entity which is responsible for the loading of any class in JVM. So each implementation of JVM comes with few pre defined class loaders. You can also find an abstract class ClassLoader which is extended by all other class loaders. Hence, it is an hierarchical system of Class Loaders.
There are various class loading models and its a huge topic for discussion, here what I present is the common one used in the JVM. The class loading works on a delegation model, which means if a class loader is requested to load a class with a given name, it will perform the following steps:
- Check with JVM if this class is already loaded. The algorithm is to check if this class loader is recorded as the Initiating Class Loader for this class.
- If no class was returned in the above step, then It checks with the parent class loader for the class name.
- If there is no parent, it will delegate the task to the bootstrap class loader
- If no class was returned in any of these steps then it might thrown a ClassNotFoundException if this class loader has not implemented the findClass method of the ClassLoader abstract class, because the default implementation just raises this exception.
The above steps demonstrates the delegation model and the model seems to be hierarchical. However, the delegation model might not be hierarchical every time, some time it can be a parallel delegation as well.
As we know that the class loading works on a delegation model, so it is not necessary that the loader which initiates the class loading also creates and defines the class. This loader is called the initiating loader. This loader may delegate the loading to another loader.
Once the initiating loader delegates the loading, there might be a loader which has actually loaded and defined the class. This loader is called the defining loader.
The Run Time Name of a class
The class loader business makes things more well defined. The JVM redefines the name of the loaded class, the name of the class is a tuple with three elements, <class loader name, package name and the simple class name>. If a class in a package is accidentally loaded by two class loaders (this can happen due to a messed up loader hierarchy), then there would be two distinct classes <CL1, PK1, class> and <CL2, PK1, class>. This can result into a ClassCastException if you are trying to type cast this class into the reference of the same class(but loaded by a different class loader).
So, we need to be very careful if we are implementing our custom class loaders.
The Sun’s implementation of Java offers many class loaders, here I mention the ones which are of interest:
Bootstrap Class Loader
You might not be able to see any Java code for this class loader in the supplied source because it is implemented in native language and this might cause it to behave differently on different platforms.
If you see the code segment from the ClassLoader#loadClass(String name, boolean Resolve) method, you can easily see that in the process of loading a class if the parent class loader for the current thread context is null, it ends up requesting the BootStrap class loader to load the class or finally return a null if the class couldn’t be located.
So where does the bootstrap class loader loads classes from?
All the core java library classes are loaded by the bootstrap class loader. For e.g.: java.lang.Object, java.lang.String and anything located in the JRE\lib\rt.jar is loaded by the bootstrap class loader.
Now because you know that the class java.lang.String was loaded by the bootstrap loader, so you can try writing String.class.getClassLoader() but this will always return a null due to its native implementation.
The loading process
When this loader is requested to load a class, the JVM first checks whether the bootstrap class loader is already recorded as the initiating class loader for this class.
If so, then no further action is required. Else, the JVM will direct the bootstrap class loader to load the class. In case the loader is not able to locate any class file or medium to get the byte array representation of the class, it will throw a ClassNotFoundException.
Extension Class Loader
As the bootstrap class loader loads the system libraries, the ExtClassLoader looks for the classes in the location given by java.ext.dirs which by default is the JRE\lib\ext . So, if you have few jars and you are using it it many applications running on the same JVM you can optionally get the classes in the jar loaded by the ExtClassLoader by simply putting it in the ext directory.
The ExtClassLoader is the parent of AppClassLoader mentioned below.
Application Class Loader
This is another important class loader which mostly is responsible for loading the classes of your application. When you run a java program, the main class and all subsequent classes are loaded by this class loader.
For e.g.: If I run a class MyClass then the statement MyClass.class.getClassLoader() will return sun.misc.Launcher$AppClassLoader and the statement MyClass.class.getClassLoader().getParent() will return sun.misc.Launcher$ExtClassLoader
The AppClassLoader looks for the classes in the class path of the application which corresponds to the path represented by the property java.class.path
The Magic behind Hot Swapping
As all other user defined class loaders (ExtClassLoader or AppClassLoader), a programmer can always write their own custom class loaders. If a class loader always polls the class path or any particular directory for last updated time or version then it can detect the changes and remove the older versions of the class with the newer files.
I made it sound very simple but it still is a complex issue and there are many issues in making a true hot swap possible. For instance, replacing all the objects with the new ones, replacing the definition of the class in the heap and many more such concerns makes it tough to achieve.
Properties of a good class loader
A developer can write her custom class loader and most of the times it is really necessary. Here are few important properties which she must take into consideration before writing a class loader:
- Given the same name, a good class loader should always return the same Class object.
- If a class loader L1 delegates loading of a class C to another loader L2, then for any type T that occurs as the direct superclass or a direct superinterface of C, or as the type of a field in C, or as the type of a formal parameter of a method or constructor in C, or as a return type of a method in C, L1 and L2 should return the same Class object.
- If a user-defined class loader prefetches binary representations of classes and interfaces, or loads a group of related classes together, then it must reflect loading errors only at points in the program where they could have arisen without prefetching or group loading.
This is a simple and good point to start learning about class loading but as I said, its a complex process and so is the delegation model. There are various other ways proposed. It would be tough to explain all of it here while maintaining the simplicity of the post.
In case you are interested in learning more about it, here is a good link to read.