Class loader

Learning objectives

Through the analysis of source code level, understand the direct relationship between the three loaders and the understanding of parental delegation mechanism


Introduction to class loader

The design team of Java virtual machine intentionally put the binary byte stream describing a class obtained through the full class name of a class into the external implementation of the jvm, so that the referencing program knows how to obtain the required class. The code that implements this action is called a class loader. The class of this code is ClassLoader, which is an abstract class. Its implementation class determines the specific loading process.


ClassLoader

To load a Class, ClassLoader is only responsible for the first stage of Class loading - loading. It loads a Class into memory in a binary way through the implementation of its subclass and converts it into Java corresponding to a target Class Lang. Class, and associate the Class object instance with the Class loader of its subclass. The relationships among classes, Class loaders, and Class instances are as follows:




Type of class load

Class loading is divided into display loading and implicit loading:

  • Display loading: class objects loaded by calling ClassLoader in code, such as directly using class Forname (name) or this getClass(). getClassLoader(). Loadclass() loads the class object.
  • Implicit loading: for objects that do not call ClassLoader to load, the jvm automatically loads the class into memory. The loaded class will still be associated with the loader used by the jvm



Type of loader

There are two types of loaders

  • The system class loader enables the jvm to run
  • User defined classloader is implemented in Java language and developed by developers themselves

jdk comes with two user-defined loaders: extension class loader and application class loader


Three kinds of loaders in jdk

Among the three loaders, the extension class loader and the application class loader are in the launcher class, and they are all static classes of this class.


logical relationship

The relationship here is their logical relationship: the logical relationship is inheritance


Code structure relationship

Relationship at the code level of the corresponding class


BootstrapClassLoader

It is called boot class loader, also known as boot class loader. It has the following characteristics:

  • Class loader written in C/C + + language
  • What is loaded is the core code in java, which is used to meet the needs of the jvm itself
  • For security reasons, it only loads package names starting with java, javax and sun
  • Logically, it is the parent class of the other two loaders
  • Use the - XX:+TraceClassLoading parameter to get the class loaded by the loader


Test: name a class and the package name is Java Lang, class name: String, see if it can run



ExtClassLoader

Called extension class loader, it has the following characteristics

  • Written in java language, it is the internal class of Launcher
  • From Java Load the class library in the directory specified by the ext.dirs system attribute, or load the class library from the jre/lib/ext subdirectory of the JDK installation directory. If the JAR created by the user is placed in this directory, it will also be automatically loaded by the extension class loader.



AppClassLoader

Also known as application loader, it has the following characteristics:

  • Written in java language, it is the internal class of Launcher
  • It is responsible for loading the environment variable classpath or the system attribute Java class. Path specifies the class library under the path
  • The class loader in the application defaults to the system class loader.
  • It is the default parent loader for user-defined class loaders
  • The ClassLoader can be obtained through the getSystemClassLoader() method of ClassLoader

Source code analysis of user-defined loader


User defined loaders include: extension class loader and reference program class loader. However, because it is source code analysis, we need to start from ClassLoader and analyze it step by step.

ClassLoader source code

Key codes:

public abstract class ClassLoader {
    // Parent class loader    
    private final ClassLoader parent;    
    
    // Name: the full class name of the class to be loaded
    // Resolve: if true, resolve the class
    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // Check whether the class is loaded
            Class<?> c = findLoadedClass(name);
            // If the class is not loaded, it is ready to be loaded
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    // If the parent loader of the loader is null, it means that the parent loader is a system class loader
                    // Directly pass the class name to the system class loader, and its bottom layer calls C + + for loading
                    // Otherwise, the parent class loader is called to load
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }
                // If the parent is still null after loading, it means that the parent loader cannot be loaded and can only be loaded by this loader
                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    // Call findClass to try to load the class
                    c = findClass(name);

                    // Count the information of which classes are loaded by this class loader
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            // Whether the class needs to be parsed. If it is true, the class will be parsed
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }
    // Let subclasses override this method, and subclasses override this method to load classes
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }
    
    // Private constructor, but when the subclass passes into the parent loader, it will always call the method upward until this method is called
    private ClassLoader(Void unused, ClassLoader parent) {
        this.parent = parent;
        if (ParallelLoaders.isRegistered(this.getClass())) {
            parallelLockMap = new ConcurrentHashMap<>();
            package2certs = new ConcurrentHashMap<>();
            domains =
                Collections.synchronizedSet(new HashSet<ProtectionDomain>());
            assertionLock = new Object();
        } else {
            // no finer-grained lock; lock on the classloader instance
            parallelLockMap = null;
            package2certs = new Hashtable<>();
            domains = new HashSet<>();
            assertionLock = this;
    }
}

The flow chart is shown in the figure:

URLClassLoader source code

URLClassLoader inherits from SecureClassLoader, but there is basically no rewriting about loading in SecureClassLoader class, so the rewriting method findClass corresponding to loading still depends on URLClassLoader.

protected Class<?> findClass(final String name)
    throws ClassNotFoundException
{
    // Results of loading classes
    final Class<?> result;
    try {
        result = AccessController.doPrivileged(
            new PrivilegedExceptionAction<Class<?>>() {
                public Class<?> run() throws ClassNotFoundException {
                    String path = name.replace('.', '/').concat(".class");
                    // Obtain resources according to the converted fully qualified class name
                    Resource res = ucp.getResource(path, false);
                    if (res != null) {
                        try {
         // Obtain a class defined by binary byte stream from the specified resource. This class can only be used after parsing
                            return defineClass(name, res);
                        } catch (IOException e) {
                            throw new ClassNotFoundException(name, e);
                        }
                    } else {
                        return null;
                    }
                }
            }, acc);
    } catch (java.security.PrivilegedActionException pae) {
        throw (ClassNotFoundException) pae.getException();
    }
    if (result == null) {
        throw new ClassNotFoundException(name);
    }
    return result;
}

Launcher source code

Two loaders and two loaders are the internal classes in this class, so before analyzing the other two loaders, you need to analyze the source code of the Launcher class in order to better analyze other loaders. The Launcher class has no source file in the downloaded jdk, only the class file. You need to download the source code of openJdk to find the corresponding Launcher class file. Documents will be shared later in the article
Key codes:

package sun.misc;

public class Launcher {
    // For variables decorated with static, new Launcher() will be executed during initialization;, This calls the constructor
    private static Launcher launcher = new Launcher();
    
    // Application Loader
    private ClassLoader loader;

    public Launcher() {
        // Create an extension class loader
        ClassLoader extcl;
        try {
            // Create an extension class loader
            extcl = ExtClassLoader.getExtClassLoader();
        } catch (IOException e) {
            throw new InternalError(
                "Could not create extension class loader", e);
        }

        try {
            
            // Create an application class loader and pass in the extension class loader. The passed in loader is the parent class loader
            loader = AppClassLoader.getAppClassLoader(extcl);
        } catch (IOException e) {
            throw new InternalError(
                "Could not create application class loader", e);
        }

        
        // There is also a series of code behind, which has nothing to do with source code analysis. It is omitted for the time being
    }
}

Launcher's package name is sun Misc starts, so the loader is loaded by the system class loader. The system class loader will load this class when the jvm starts. And because static decorated objects are created, they will also be initialized and the construction method will be executed.
In the construction method, the loader performs two things:

  • Create extension class loader
  • Create application class loader

ExtClassLoader source code


 static class ExtClassLoader extends URLClassLoader {
     // Create an extension class loader, key code: return new ExtClassLoader(dirs);
     public static ExtClassLoader getExtClassLoader() throws IOException
    {
        final File[] dirs = getExtDirs();

        try {
            return AccessController.doPrivileged(
                new PrivilegedExceptionAction<ExtClassLoader>() {
                    public ExtClassLoader run() throws IOException {
                        int len = dirs.length;
                        for (int i = 0; i < len; i++) {
                            MetaIndex.registerDirectory(dirs[i]);
                        }
                        return new ExtClassLoader(dirs);
                    }
                });
        } catch (java.security.PrivilegedActionException e) {
            throw (IOException) e.getException();
        }
    }
     
     
  // The loadClass method of ClassLoader is not overridden, so the method of ClassLoader is used
  // The findClass method in URLClassLoader is not overridden, so the method of the parent class is used
 }

Source code analysis of AppClassLoader

static class AppClassLoader extends URLClassLoader {
    public Class<?> loadClass(String name, boolean resolve)
       throws ClassNotFoundException
   {
       int i = name.lastIndexOf('.');
       if (i != -1) {
           SecurityManager sm = System.getSecurityManager();
           if (sm != null) {
               sm.checkPackageAccess(name.substring(0, i));
           }
       }

       if (ucp.knownToNotExist(name)) {
           // A class with the given name could not be found in the parent class loader and its local URLClassPath.
           // Check whether this class has been dynamically defined;
           // If yes, return the loaded class; Otherwise, skip the parent delegate and findClass.
           Class<?> c = findLoadedClass(name);
           if (c != null) {
               if (resolve) {
                   resolveClass(c);
               }
               return c;
           }
           throw new ClassNotFoundException(name);
       }

       return (super.loadClass(name, resolve));
   }
  // The findClass method in URLClassLoader is not overridden, so the method of the parent class is used
    
    
}

You can see that although the loadClass is rewritten, the loadClass method of the parent loader will be called in the end.
There are also two construction methods. The screenshot is as follows:
![image.png](https://img-blog.csdnimg.cn/img_convert/f400cf036fa9b44ff7f5904a8be24327.png#clientId=u75fb6ee7-1107-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=390&id=u085c84c0&margin=[object Object]&name=image.png&originHeight=526&originWidth=1343&originalType=binary&ratio=1&rotation=0&showTitle=false&size=135272&status=done&style=none&taskId=u68af0ff0-3e5f-42cf-a8d4-8d2a4d17041&title=&width=994.814885091067)


Source code summary

  • There is a loadClass method in ClassLoader. This method is overridden by AppClassLoader, but ExtClassLoader does not override this method
  • SecureClassLoader only wrote some secure implementations, but did not implement loading
  • The URL rewrites the findClass method in ClassLoader, which has not been rewritten by the other two loaders
  • The Launcher creates two loaders. When creating the AppClassLoader loader, the ExtClassLoader created in the first step will be passed into its creation method as the parent loader.
  • ExtClassLoader does not override the loadClass and findClass methods, but uses the parent class of
  • AppClassLoader only rewrites loadClass, but it may call the loadClass method of the parent class

So:

Both loaders will call the loadClass method in ClassLoader, and the loadClass method is to load by itself without loading, and let the parent loader load. If the parent loader cannot load, try to load by itself. This phenomenon is called parental delegation mechanism



Parental delegation mechanism

Refer to the source code of ClassLoader in the source code analysis of user-defined loader, and the loadClass method is the implementation of parent delegation mechanism. You can also see the source code summary above, which is a summary of the parental delegation mechanism


References and related resources

Related resources: https://download.csdn.net/download/xing_S/82487214
Reference: "in depth understanding of java virtual machine", the jvm of song Hongkang, a teacher from Silicon Valley

Keywords: Java jvm Back-end

Added by paulbrown83 on Sat, 26 Feb 2022 14:21:49 +0200