Java Virtual Machine Internals, Part 1: Class Loader

The Java Virtual Machine is the heart of the Java ecosystem. Thanks to the JVM, when it comes to Java programs, we can 'write once, run everywhere.' Like other virtual machines, the JVM is also an abstract computer. The Java Virtual Machine's main job is to load class files and execute the bytecode they contain.

There are multiple components of the Java Virtual Machine like class loader, the garbage collector (automatic memory management), interpreter, JIT compiler, thread management. In this series,  I’ll be discussing how the Java Virtual Machine works. In this first installment, we are going to talk about the class loader. So let's get started!

The class loader loads class files from both the program and the Java API. Only those class files from the Java API that are actually needed by a running program are loaded into the virtual machine.

The bytecodes are executed in an execution engine.

Image title

What Is Class Loading?

Class loading is finding and loading types (classes and interfaces) at runtime dynamically. Types data are contained in binary files in class file format.

Phases of Class Loading

The class loader subsystem is responsible for more than just locating and importing the binary data for classes. It must also verify the correctness of imported classes, allocate and initialize memory for class variables, and assist in the resolution of symbolic references. These activities are performed in a strict order:

Note: In addition to loading classes, a class loader is also responsible for locating resources. A resource is some data (a ".class" file, configuration data, or an image for example) that is identified with an abstract '/'-separated path name. Resources are typically packaged with an application or library so that they can be located by code in the application or library.

The Java Class Loading Mechanism

The Java platform uses a delegation model for loading classes. The basic idea is that every class loader has a "parent" class loader. When loading a class, a class loader first "delegates" the search for the class to its parent class loader before attempting to find the class itself.

The class loader delegation model is the graph of class loaders that pass loading requests to each other. The bootstrap class loader is at the root of this graph. Class loaders are created with a single delegation parent and look for a class in the following places:

A class loader first determines if it has been asked to load this same class in the past. If so, it returns the same class it returned last time (that is, the class stored in the cache). If not, it gives its parent a chance to load the class. These two steps repeat recursively and depth first. If the parent returns null (or throws a ClassNotFoundException), then the class loader searches its own path for the source of the class.

Because the parent class loader is always given the opportunity to load a class first, the class is loaded by the class loader nearest the root. This also has the effect of only allowing a class loader to see classes loaded by itself or its parent or ancestors; it cannot see classes loaded by its children.

The Java SE Platform API historically specified two class loaders:

Run-Time Built-in Class Loaders (JDK 9+)

The three built-in class loaders work together to load classes as follows:

To see the built-in class loaders, you can run the below code:

public class BuiltInClassLoadersDemo {

    public static void main(String[] args) {
        BuiltInClassLoadersDemo demoObject = new BuiltInClassLoadersDemo();
        ClassLoader applicationClassLoader = demoObject.getClass().getClassLoader();
        printClassLoaderDetails(applicationClassLoader);

        // java.sql classes are loaded by platform classloader
        java.sql.Date now = new Date(System.currentTimeMillis());
        ClassLoader platformClassLoder = now.getClass().getClassLoader();
        printClassLoaderDetails(platformClassLoder);

        // java.lang classes are loaded by bootstrap classloader
        ClassLoader bootstrapClassLoder = args.getClass().getClassLoader();
        printClassLoaderDetails(bootstrapClassLoder);
    }

    private static void printClassLoaderDetails(ClassLoader classLoader){
        // bootstrap classloader is represented by null in JVM
        if(classLoader != null) {
            System.out.println("ClassLoader name : " + classLoader.getName());
            System.out.println("ClassLoader class : " + classLoader.getClass().getName());
        }else {
            System.out.println("Bootstrap classloader");
        }
    }
}


With Amazon Corretto 11.0.3 installed on my machine, the above code produces the following output:

ClassLoader name : app
ClassLoader class : jdk.internal.loader.ClassLoaders$AppClassLoader 
ClassLoader name : platform 
ClassLoader class : jdk.internal.loader.ClassLoaders$PlatformClassLoader 
Bootstrap classloader


You can check the ClassLoader APIs here (JDK 11).

Note: HotSpot VM (via Amazon Corretto 11 ) is used as reference JVM implementation in this series. 

Stay tuned for part 2 where we take a closer look at JVM internals and the class file format.

 

 

 

 

Top