JVM class loading mechanism

Posted by Xadian on Tue, 28 Dec 2021 06:20:00 +0100

From the overall logic of the JVM in the previous article, this article will talk about the class loading mechanism

Review some working models: one we wrote ourselves java file, compiled and generated Class file, will The class file is loaded into the JVM according to The different data information of the class binary file is allocated to different positions in the runtime data area. According to the information of the stack, it is handed over to the java executor step by step to execute the code. In the process of execution, it is dynamically regenerated into information and put into the runtime data area

It is executed by three parts: class loading system, runtime data area and java executor

This article mainly describes some understanding of the class loading system

1. Overall process

1. Compiled file
2. Run file
3. start-up java virtual machine
4. adopt C++Code creation Bootstrap Class loader,This loader is C++realization
5. adopt Bootstrap load Launcher This class
6. adopt Laucher establish java of Extension and App loader   appClassLoader and ExtClassLoader by Laucher Inner class of
7. Attempted loading via loader parental delegation mechanism,Analysis and other series
8. Load complete,adopt main Method call

2. Process of loadclass

In the source code, loadclass is an overall process, including preparations, the use of three class loaders (judge whether this. Class can be loaded through the parental training mechanism), and Class binary file is read through byte stream to allocate memory to runtime data area

1. Load the binary file into memory and put it into the runtime data area for the following steps

load >> verification >> prepare >> analysis >> initialization >> use >> uninstall

Load: find and pass on the hard disk IO Read in the bytecode file and load it only when the class is used, such as calling the class main()method, new Object and so on. In the loading phase, an object representing this class will be generated in memory java.lang.Class Object as the access entry of various data of this class in the method area

Verification: verify the correctness of bytecode file

Preparation: allocate memory to static variables of the class and assign default values

Parsing: replace symbolic references with direct references. In this stage, some static methods will be used(Symbol references, such as main()method)Replace with a pointer or handle to the memory where the data is stored(Direct reference),This is the so-called static linking process(Completed during class loading),Dynamic link is completed during the program running, replacing symbolic reference with direct reference. The next lesson will talk about dynamic link

Initialization: initialize the static variable of the class to the specified value and execute the static code block

Mainly by URLClassLoader Class defineClass complete,Our load resolution,A series of verification processes are completed here,At the same time, in this process, the unique of this class will be created Class object

2. Prepare class loader and parent delegation mechanism before loading

The main work here is the preparation. Before loadclass, judge whether this class can be loaded through the preparation. The preparation involves three class loaders. How do these three class loaders complete the preparation? This is the parental training mechanism

//The loadClass method of ClassLoader implements the parental delegation mechanism
protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            // Check whether the current class loader has loaded the class
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    //If the parent loader of the current loader is not empty, delegate the parent loader to load the class
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {  //If the current loader parent 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) {
                resolveClass(c);
            }
            return c;
        }
 
Parental delegation mechanism:
From bottom to top, check whether the loaded class has been loaded
 Try to check whether the current class to be loaded is a file in the path you want to load from top to bottom,Load if you can,Can't load to subclass

Why should parents delegate?

Sandbox security mechanism: written by myself java.lang.String.class Class is not loaded, which prevents the core from being loaded API The library was tampered with at will
​
Avoid repeated loading of classes: when the father has loaded the class, there is no need to add children ClassLoader Load again to ensure the uniqueness of the loaded class

Overall responsibility entrustment mechanism

""Overall responsibility" means being a ClassLoader When loading a class, unless the displayed uses another class ClassLoader,The classes that this class depends on and references are also controlled by this ClassLoader Load.
​
public class Math {
    public static final int initData = 666;
    public static User user = new User();
}
If Math from appclassLoader Load,Then the properties in this class, etc  User Classes are also loaded by this loader

4. Custom class loader

public class MyClassLoaderTest {
    static class MyClassLoader extends ClassLoader {
    private String classPath;
​
    public MyClassLoader(String classPath) {
    this.classPath = classPath;
    }
​
    private byte[] loadByte(String name) throws Exception {
        name = name.replaceAll("\\.", "/");
        FileInputStream fis = new FileInputStream(classPath + "/" + name
        + ".class");
        int len = fis.available();
        byte[] data = new byte[len];
            fis.read(data);
        fis.close();
        return data;
    }
​
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            byte[] data = loadByte(name);
            //defineClass converts a byte array into a class object. This byte array is the final byte array after the class file is read.
            return defineClass(name, data, 0, data.length);
            } catch (Exception e) {
                e.printStackTrace();
            throw new ClassNotFoundException();
        }
    }
}

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 two parent delegation mechanism, and the other is findClass, which is empty by default. Therefore, our custom class loader mainly rewrites the findClass method

Our custom class loader should also strictly abide by the two parent delegation mechanism. Upward delegation is completed in the loadClass of ClassLoader, which does not need us to implement. jvm has completed setting the parent loader of the custom loader as appClassLoader, so we can naturally complete upward delegation. What we have completed is downward loading, but it is not loaded in the upper class loader, It's the custom loader's turn to load

When will the appClassLoader fail to load, and then it will be the custom class loader's turn to load?

1. Identify a problem    
Booststrap   Extension App All three have their own loading paths
BootStrap:   jdk Under the installation directory/jre/lib/Some of jar
Extension:   jdk Under the installation directory/jre/lib/ext    Can also be -Djava.ext.dirs appoint
App:         Load environment variable classpath The path in the file and execute the file through the parameters  java -cp XXX Path file passed in
2. As can be seen from the above,App The loader also has a fixed path ,It won't change after the program starts,Then, as long as our custom loader does not overlap the path of the upper loader,During downward loading,The upper loader is not loaded into the,Naturally, it's our turn to load the custom loader
    MyClassLoader classLoader = new MyClassLoader("D:/test");
    //When creating objects, our custom loader can pass the path that can be loaded in this way, or write it in the construction method
 be careful:   The path must not overlap with the upper layer.

Example: in linux, we compiled the How is the class file parsed?

export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
 We configured the environment variable.    Indicates that the current execution path can be App Load
​
[root@node01 test]# java /usr/local/tmp/demo
Error: Could not find or load main class .usr.local.tmp.demo
 When we do not execute the.class    Will not be App Loader loading,Natural execution failed
​
export CLASSPATH=$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:/usr/local/tmp
 When the environment variable is configured like this,In addition to the above folders.class  The rest cannot be loaded

How is the parent of the custom class loader specified

MyClassLoader classLoader = new MyClassLoader("D:/test");
//When this custom loader is initialized, the ClassLoader will be initialized first
 use super(parent)appoint
 Custom classLoader No construction method,Parameterless constructor of the parent class accessed by default
 here getSystemClassLoader It means Appclassloader,Write dead
       protected ClassLoader() {
        this(checkCreateClassLoader(), getSystemClassLoader());
    }
and Ext and App yes parent Not by ClassLoader Set by the parameterless constructor of the class

5. Break the parental appointment mechanism

It can be seen from the custom loader that the parental delegation mechanism is strictly followed, and the parental delegation mechanism is completed by the classloader in loadClass
 Then our custom class loader has inherited loadclass. We just need to rewrite the classloader and remove the judgment code of parent delegation
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;
        }
}
​
public static void main(String args[]) throws Exception {
    MyClassLoader classLoader = new MyClassLoader("D:/test");
    //Try to use your own rewriting class loading mechanism to load your own Java jvm. User class
    Class clazz = classLoader.loadClass("java.jvm.user");
    Object obj = clazz.newInstance();
    Method method= clazz.getDeclaredMethod("sout", null);
    method.invoke(obj, null);
    System.out.println(clazz.getClassLoader().getClass().getName());
    }
 }
//We put a copy in the classpath of the App loader and a copy in the position of disk D to cancel the code delegated by our parents and check the results
 result:
java.io.FileNotFounddException: D:\test\java\lang\Object.class   (The system cannot find the specified path.)
All classes are inherited from Object Class,The parental delegation mechanism was broken,We can only start from D:test Load under folder Object class
 therefore,We want to directly Object Class to this folder,Let our custom class loader load it
 result:
java.lang.SecurityException: Prohibited package name: java.lang
    at java.lang.ClassLoader.preDefineClass(ClassLoader.java:659)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:758)

Sandbox security mechanism

Bytecode verifier, class loader
 After completing the sandbox security mechanism, the jdk core package cannot be loaded by the custom class loader, and the core package of some classes such as String Object

So how to break

Modify the loadClass method to let our custom class break the ring and the parent delegation. The loading under the core package is still completed by the parent delegation
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();
            //Judge here
            //If this class is not the class we want to break the parental delegation, it will be executed through the loadClass parental delegation of ClassLoader
            //If it's our own, it can be broken so that the Object can be loaded by the BootStrap loader
            if(!name.startsWirh("com.jvm")) {
                c = this.getParent().loadClass(name)
            }else{             
                c = findClass(name);
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
        }
}

6.Tomcat breaks parental delegation

1. A web container may need to deploy two applications. Different applications may depend on different versions of the same third-party class library. It is not necessary 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 independent and isolated from each other. For example, there are two war packages, one is spring4 and the other is spring5. It can be seen from parent delegation that classes with the same class name and package name cannot be loaded repeatedly
​
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, 10 copies of the same class library 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.
​
How to solve the above problems?
Create a class loader for each war package class independently. Each independent class loader can only load classes under its own independent path

1. The JVM sandbox security class is loaded by the JVM App Ext Bootstrap for parent delegation
 2. Inside tomcat, the Tomcat classes required by each war package are loaded by the dotted line box. When they reach the dotted line, they are no longer delegated to the upper parents
 3. Our own war package is loaded by each independent WebappclassLoader without upward parent delegation
Several main class loaders of tomcat:
commonLoader: Tomcat's most basic class loader. The classes in the loading path can be accessed by the Tomcat container itself and various webapps;
catalinaLoader: the private class loader of Tomcat container. The classes in the loading path are not visible to Webapp;
sharedLoader: the class loader shared by each Webapp. The classes in the loading path are visible to all webapps, but not to the Tomcat container;
WebappClassLoader: the private class loader of each Webapp. The classes in the loading path are only visible to the current Webapp, such as loading the relevant classes in the war package. Each war package application has its own
 WebappClassLoader realizes mutual isolation. For example, different war package applications have introduced different spring versions, so that the implementation can load their own spring versions;

Topics: Java JavaEE jvm Spring intellij-idea