Jvm class loading mechanism

Operation process of class

Take a main method as an example:

The specific process of class loading is as follows:


1. Load: load the class bytecode file into memory through the class loader

2. Verification: verify whether the bytecode file conforms to the jvm specification

3. Preparation: assign initial values to static variables
For example, the initial value of 8 basic types is 0 by default; The initial value of reference type is null; The initial value of the constant is the value set in the code. If final static tmp = 456, the initial value of tmp in this stage is 456

4. Parsing: convert symbol reference to direct reference
Symbol reference. That is, a string, but this string gives some relevant information that can uniquely identify a method, a variable and a class.
Direct reference. It can be understood as a memory address or an offset. For example, for class methods, the direct reference of class variables is a pointer to the method area; The direct reference of instance method and instance variable is the offset from the head pointer of the instance to the position of the instance variable
For example, if you call the method hello(), the address of this method is 1234567, then Hello is a symbolic reference and 1234567 is a direct reference.
In the parsing phase, the virtual opportunity replaces all symbolic references such as class name, method name and field name with specific memory address or offset, that is, direct reference.
If it is resolved during class loading, it is "static link", and it is "dynamic link" when the code is run to the corresponding code line.
for instance:

Click to view the code
public static void main(String[] args) { 
Math math = new Math();
math.compute();
}

The main method is put into memory when the class is loaded, which is a static link; math.compute() is loaded only when it runs to this line. It is a dynamic link.

5. Initialization: initialize the static variable of the class to the specified value and execute the static code block
If the parent class of a class is not initialized when it is initialized, its parent class will be initialized first.
If multiple static variables and static code blocks are included at the same time, they are executed in top-down order.

Note: class loading in java is lazy loading. When a jar package is started, it will not load all classes. It will be loaded only when it is used.
for instance:

Click to view the code
public class Main4 {


    static {
        System.out.println("*************load TestDynamicLoad************");
    }

    public static void main(String[] args) {
        new A();
        System.out.println("*************load test************");
        B b = null;
    }
}

class A {
    static {
        System.out.println("*************load A************");
    }

    public A() {
        System.out.println("*************initial A************");
    }
}

class B {
    static {
        System.out.println("*************load B************");
    }

    public B() {
        System.out.println("*************initial B************");
    }

}

Operation results:

Click to view the code
*************load TestDynamicLoad************
*************load A************
*************initial A************
*************load test************

If B = null in the main method; Modify to B = new b();, The running result will change to:

Click to view the code
*************load TestDynamicLoad************
*************load A************
*************initial A************
*************load test************
*************load B************
*************initial B************

Class loader and parental delegation

Class loader

Class loaders in java include boot class loader, extension class loader, application class loader and user-defined class loader;
1. Boot class loader: responsible for loading the core class library under the lib directory of jre;
2. Extended class loader: it is responsible for loading jar packages under the lib directory and ext directory of jre;
3. Application class loader: responsible for loading the class package under the ClassPath path (self written classes);
4. Custom class loader: it is responsible for loading the class package under the user-defined path.

Parental delegation

The specific flow of parent delegation is shown in the following figure. When loading a class, you will first judge whether this class has been loaded from AppClassLoader. If it has not been loaded, you will delegate the parent class loader to judge; If they have not been loaded, the parent class will determine whether they can be loaded first. If the parent class can be loaded, it will be loaded by the parent class, otherwise it will be loaded by the child class.
The parent delegation mechanism simply means that the parent class loads first, and the parent class cannot be loaded by the child class.

The benefits are:
1. Sandbox security: the class written by yourself will not overwrite the class with the same name in the core library. For example, write a Java Since the classloader class has the same name, it will not be tampered with by the classloader class. Therefore, it will not be injected from the classloader class to a certain extent.
2. Avoid repeated loading of classes;

Overall responsibility entrustment mechanism
ClassLoader uses the overall responsibility delegation mechanism to load classes. "Overall responsibility" means that when a ClassLoder loads a class, unless another ClassLoder is used, the classes that the class depends on and references are usually loaded by this ClassLoder.

The implementation code is as follows:

Click to view the code
//The loadClass method of ClassLoader implements the parental delegation mechanism
    protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException {
        synchronized (getClassLoadingLock(name)) {
            // Check whether the current class loader has loaded the class
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) { //If the parent loader of the current loader is not empty, delegate the parent loader to load the class
                        c = parent.loadClass(name, false);
                    } else { //If the parent loader of the current loader is empty, delegate the boot class loader to load the class
                        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();
                    //Will call the findClass method of URLClassLoader to find and load the class in the classpath of the loader
                    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) { //Will not execute
                resolveClass(c);
            }
            return c;
        }
    }

Breaking parental delegation

First look at the custom class loader: the custom class loader only needs to inherit Java Lang. classloader class, which has two core methods, one is loadClass(String, boolean), which implements the parental delegation mechanism, and the other is findClass, which is empty by default. Generally speaking, our custom class loader mainly rewrites the findClass method. If we want to break the parental delegation, we need to override the loadClass method.

By observing the code of parent delegation above, we can see that if (parent! = null) {C = parent.loadclass (name, false);} else { c = findBootstrapClassOrNull(name); } You can break the parental delegation.

The implementation code is as follows:

Click to view the code
    /**
     * Rewrite the class loading method to implement its own loading logic and do not delegate it to parents
     *
     * @param name
     * @param resolve
     * @return
     * @throws ClassNotFoundException
     */
    protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);

            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                long t1 = System.nanoTime();
                c = findClass(name);

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

Can't you use Tomcat by default?

Let's think about it: Tomcat is a web container, so what problems does it want to solve:

  1. A web container may need to deploy two applications, and different applications may rely on the same third-party class library
    For different versions, it is not required to have only one copy of the same class library on the same server. Therefore, it is necessary to ensure that the class libraries of each application are the same
    Independent and isolated from each other.
  2. Deployed in the same web container, the same class library and the same version can be shared. Otherwise, if the server has 10 applications
    Order, then 10 copies of the same class libraries should be loaded into the virtual machine.
  3. The web container also has its own dependent class library, which can not be confused with the class library of the application. For safety reasons, the container should be
    The class library is isolated from the class library of the program.
  4. The web container should support the modification of jsp. As we know, the jsp file must be compiled into a class file before it can be used in the virtual machine
    But it is common to modify jsp after the program is running. The web container needs to support jsp modification without restarting.

Therefore, Tomcat cannot use the default parental delegation. Why?
First, if you use the default class loader mechanism, you cannot load different versions of the same class library
No matter what version you are, your class adder only cares about your fully qualified class name, and there is only one copy.
The second problem is that the default class loader can be implemented because its responsibility is to ensure uniqueness.
The third question is the same as the first.
Let's look at the fourth question. We think how to realize the hot loading of jsp files. jsp files are actually class files
If it is modified, but the class name is still the same, the class loader will directly take the existing and modified jsp in the method area
It will not be reloaded. So what? We can uninstall the classloader of this jsp file directly, so you should think
At that time, each jsp file corresponds to a unique class loader. When a jsp file is modified, it will be unloaded and loaded directly
Device. Recreate the class loader and reload the jsp file.

Keywords: Java

Added by psquillace on Thu, 10 Mar 2022 16:32:52 +0200