1. Class loading mechanism of JVM

Posted by kybaker on Mon, 22 Nov 2021 23:15:28 +0100

1. Flow chart of loading classes in java.

Main steps:

  • Load: find the bytecode file on the hard disk and read it through IO. It will be loaded only when the class is used. For example, call the main() method and new object of the class. In the loading stage, a java.lang.Class object representing the class will be generated in memory as the access entry for various data of the class in the method area.
  • Verification: verify the accuracy of bytecode file.
  • Preparation: allocate memory to the static variables of this class and assign default values.
  • Parsing: replace symbolic references with direct references. In this stage, some static methods (symbolic references, such as main() method) will be replaced with pointers or handles pointing to the memory stored in the data (direct references). This is the so-called static linking process (completed during class loading). Dynamic linking is completed during program running, replacing symbolic references with direct references.
  • Initialization: initialize the static variable of the class to the specified value and execute the static code block.

Note: if the main class uses other classes during operation, these classes will be loaded step by step. The classes in the jar package or war package are not loaded all at once. They are loaded only when they are used.

2. Corresponding class loader in Java

  • Boot class loader: it is responsible for loading the core class libraries that support the operation of the JVM and are located in the lib directory of JRE, such as rt.jar, charsets.jar, etc.
  • Extension class loader: it is responsible for loading the JAR class package in the ext extension directory under the lib directory of JRE that supports the operation of the JVM.
  • Application class loader: it is responsible for loading the class package under the ClassPath path, mainly those classes written by yourself.
  • Custom class loader: it is responsible for loading the class package under the user-defined path.

Flowchart of class loader creation:

The relevant source code of the Launch class is as follows:

    // Calling getLauncher returns an instance object of the Launcher class, which will be created by the JVM
    public static Launcher getLauncher() {
        return launcher;
    }    
    
    // This constructor will be called when the Launcher instance object is created
    /**
    * 1,
    */
    public Launcher() {
        Launcher.ExtClassLoader var1;
        try { 
            /**
            *  An ExtClassLoader (extension class loader) is created, and the ExtClassLoader will be used in this method 
              *  parent Property, which will be directly assigned to null
            */
            var1 = Launcher.ExtClassLoader.getExtClassLoader();
        } catch (IOException var10) {
            throw new InternalError("Could not create extension class loader", var10);
        }

        try {
           /**
            *  An AppClassLoader (extension class loader) is created, and AppClassLoader will be used in this method
              *  parent Property, which will be directly assigned to the ExtClassLoader created above, and the * loader property of this class will be assigned to the instance object of AppClassLoader
            */
            this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
        } catch (IOException var9) {
            throw new InternalError("Could not create application class loader", var9);
        }
        //Set the class loader of the current thread to AppClassLoader
        Thread.currentThread().setContextClassLoader(this.loader);
        // . . .  . . .   Omit some code that you don't need to pay attention to
    }

<u> The main logic of the above code is to create two class loaders, ExtClassLoader and AppClassLoader, and assign null attribute of the corresponding parent and instance object of ExtClassLoader respectively</ u>

3. Parental delegation mechanism

The main class used for class loading in Java is java.lang.ClassLoader#loadClass(java.lang.String), so to see how the parent delegation mechanism works, we need to look at the logic corresponding to the loadClass method. The main source code of loadClass method is as follows:

    public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }

     protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // Here, we will first judge whether the current class has been loaded. If it has been loaded, null will not be returned
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    // First, we will judge whether the parent class loader of the current class is empty. When we first come in, our type loader is AppClassLoader, calling the parent class ExtClassLoader.loadClass, and when ExtClassLoader calls, parent == null
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        // Currently, the corresponding parent for ExtClassLoader is null
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    // If the previous corresponding parent class is not loaded, the findClass method will be called. This method is an empty implementation and is implemented by the corresponding child class.
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

The parent delegation mechanism is simpler, that is, find the parent class to load first, and then the son loads it himself.

Let's look at the source code of the parent delegation mechanism of the application class loader AppClassLoader loading class. The loadClass method of AppClassLoader will eventually call the loadClass method of its parent ClassLoader. The general logic of this method is as follows:

  1. First, check whether the class with the specified name has been loaded. If it has been loaded, it does not need to be loaded and returns directly.
  2. If this class has not been loaded, judge whether there is a parent loader; If there is a parent loader, it will be loaded by the parent loader (that is, call parent.loadClass(name,false); or call the bootstrap class loader to load.
  3. If neither the parent loader nor the bootstrap class loader can find the specified class, call the fIndClass method of the current class loader to complete the class loading.

<u> As can be seen from the above code, when calling the loadClass method of AppClassLoader, the parent class will be called first, and then the parent class will call the parent class until the parent == null is called. If the corresponding parent class is not loaded, it will be loaded by itself. This is the logic of the corresponding two parent delegation</ u>

4. Why design a parental delegation mechanism

  • Sandbox security mechanism: prevent the JDK core API class library from being tampered with externally. For example, the java.lang.String.class class written by ourselves will not be loaded.
  • Avoid repeated loading of classes: when the parent class has been loaded once, it is not necessary to load the child class again to ensure the uniqueness of the loaded class.

Example:

//If the package name of a custom String class is consistent with its own String, the following error will be reported.
package java.lang;

public class String {

    public static void main(String[] args) {
        System.out.println("**************My String Class**************");
    }

}

Operation results:
error: In class java.lang.String Not found in main method, Please main Method is defined as:
   public static void main(String[] args)
otherwise JavaFX Application classes must be extended javafx.application.Application

5. Custom class loader

As can be seen from the source code of the ClassLoader class above, the custom class loader only needs to inherit the java.lang.ClassLoader class. This class has two core methods, one is loadClass(String,boolean), which implements the two parent delegation mechanism, and the other is findClass, which is empty by default. Therefore, our custom class loader mainly rewrites the findClass method.

Example:

// To be added

5.1 break the parental delegation mechanism

Another example of sandbox security mechanism is to try to break the parental delegation mechanism and load our own java.lang.String.class with a custom class loader

// To be added

6. Can the two parent delegation mechanism be used to load jar or war packages in tomcat? Why?

From the implementation of tomcat, the custom class loader in tomcat, which is responsible for loading the jar of the corresponding deployment project or the corresponding class in the war package, breaks the parental delegation mechanism, and the custom class loader of Tomcat corresponding to each project deployed in the same Tomcat is independent, that is, there will be one for each project. The reasons are as follows:

Let's assume that there are two projects. One project uses spring expression version 4.3, and the other project uses spring expression version 5.5.

  • If the two parent delegation mechanism is used in tomcat, when we need to load the classes used by spring expression, the first one of the two projects will be loaded first (used and then loaded), which will cause the parent class of the later project to be found and loaded during loading, so it will not be loaded, so there will be a problem here, As a result, the version of spring expression to be used by the following project is not specified by its own project.
  • If all projects deployed in tomcat share the same class loader, the above problem will also occur.

7. This paper mainly focuses on the following five links:

  • How to load a class in java, what are the main steps, and what is the role of each step?
  • What are the class loaders in java and how are they created?
  • How to implement the parental delegation mechanism in java? Why design a parental delegation mechanism?
  • How to customize a class load and implement a custom class loader to break the corresponding parental delegation mechanism.
  • Think about whether the two parent delegation mechanism can be used to load jar s or war packages in tomcat? Why?

Topics: Java jvm