Class loader

The classloader implements getting the binary stream of a class by its fully qualified name. This action is implemented outside the Java virtual machine so that the application can decide how to get the required implementation classes.

Uniqueness of a Class: Only the Class loader that loads it and the Class itself are unique in the Java virtual machine. Even if two classes come from the same Class file and are loaded by the same virtual machine, as long as their Class loaders are different, the two classes must be different

public class ClassLoadTest {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        ClassLoader classLoader = new ClassLoader() {

            @Override
            publicClass<? > loadClass(String name)throws ClassNotFoundException {
                try {
                    String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
                    InputStream inputStream = getClass().getResourceAsStream(fileName);
                    if (inputStream == null) {
                        return super.loadClass(name);
                    }

                    byte[] b = new byte[inputStream.available()];
                    inputStream.read(b);
                    return defineClass(name,b,0,b.length);
                } catch (IOException e) {
                    e.printStackTrace();
                    throw newClassNotFoundException(name); }}}; Object obj = classLoader.loadClass("chap07.ClassLoadTest").newInstance();
        System.out.println(obj.getClass());
        System.out.println(obj instanceofchap07.ClassLoadTest); }}Copy the code

Running results:

Application class loader

Parental delegation model

From the perspective of the Java virtual machine, there are only two different class loaders: the Bootstrap ClassLoader, which is implemented in C++ and is part of the virtual machine itself. The other is other class loaders that are implemented in the Java language, independent of the virtual machine, and all inherit from the abstract java.lang.classloader class.

From a Java developer’s perspective, most Java programs use the classloaders provided by the system in the following three.

  • Bootstrap ClassLoader: Bootstrap ClassLoader: As described earlier, this class handler is responsible for storing it in the $JAVA_HOME/lib directory, or in the path specified by the -xbootCLASspath parameter, and is recognized by the virtual machine (only by filename, as in rt.jar, Libraries with incorrect names will not be loaded even if placed in the lib directory.) Libraries are loaded into the virtual machine memory. The boot class loader cannot be directly referenced by Java programs. If you need to delegate the loading request to the boot class loader when writing a custom class loader, you can use NULL instead
  • Extension ClassLoader: This loader is called sun.misc.LauncherAll libraries in the JAVA_HOME/lib/ext directory, or in the path specified by the java.ext.dirs system variable, can be used directly by the developer using the extended class loader.
  • Application ClassLoader: This ClassLoader is implemented by sun.misc.Launcher$AppClassLoader. Since this ClassLoader is the return value of the getSystemClassLoader() method in ClassLoader, it is also commonly referred to as the system ClassLoader. It is responsible for loading the libraries specified on the user’s ClassPath. Developers can use this class loader directly, and this is generally the default class loader if the application does not have its own custom class loader.

All of our applications are loaded by a combination of these three types of loaders, including our own class loaders if necessary. The relationship between these classloaders is generally shown below:

This hierarchical relationship is called the Parents Delegation Model. The parent delegate model requires that all class loaders have their own parent class loaders, except for the top-level start class loaders. The parent-child relationship between class loaders is not typically implemented in an Inheritance relationship, but in a Composition relationship that replicates the code of the parent loader.

The working process of the parental delegation model is: If a classloader receives a classload request, it does not try to load the class itself at first. Instead, it delegates the request to the parent classloader. This is true at every level of classloaders, so all load requests should eventually be passed to the top level of the starting classloader. Only when the parent loader reports that it cannot complete the load request (it did not find the desired class in its search scope) will the child loader attempt to load it itself.

One of the obvious benefits of using the parent-delegate model to organize relationships between class loaders is that Java classes have a hierarchical relationship with priority along with their classloaders. For example, the java.lang.Object class, which is stored in rt.jar, is ultimately delegated to the boot class loader at the top of the model by any classloader. Therefore, the Object class is the same class in the various classloader environments of the program. Instead of using the parent delegate model and loading by class loaders, if a user writes a class called java.lang.Object and places it in the program’s ClassPath, multiple Object classes will appear. The most basic behavior in the Java type system is not guaranteed, and the application becomes a mess. If you are interested, try writing a Java class with the same name as an existing class in the Rt.jar library, and you will find that it compiles normally, but will never be loaded and run.

The parent delegate code is all in the loadClass() method of java.lang.classLoader

protectedClass<? > loadClass(String name,boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // First, check whether the requested class has already been loaded
        Class c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if(parent ! =null) {
                    c = parent.loadClass(name, false);
                } else{ c = findBootstrapClassOrNull(name); }}catch (ClassNotFoundException e) {
                // If the parent class loader throws a ClassNotFoundException
                // Indicates that the parent class loader cannot complete the load request
            }

            if (c == null) {
                // Call its own findClass method to load
                long t1 = System.nanoTime();
                c = findClass(name);

                // this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); }}if (resolve) {
            resolveClass(c);
        }
        returnc; }}Copy the code
  1. First, check to see if the class with the specified name has already been loaded. If it has been loaded, it does not need to be loaded again and returns directly.
  2. If this class has not been loaded, then check if there is a parent loader; If there is a parent loader, it is loaded by the parent (that is, call parent-loadclass (name, false);) . Or call the bootstrap class loader to load it.
  3. If neither the parent nor the Bootstrap class loader can find the specified class, the findClass method of the current class loader is called to complete the class loading.