2. Class loading subsystem

Time:2022-5-14

1 overview of memory structure

In the previous chapter, we learned about the overall structure of the JVM. Now we understand its structure in more detail

2. Class loading subsystem

image.png

2. Class loading subsystem

2.1 function of class 1 loader subsystem

  • The classloader subsystem is responsible for loading class files from the file system or the network, the class file has a specific file ID at the beginning of the file.
  • Classloader is only responsible for loading the class file. Whether it can run or not is decided by the execution engine.
  • The loaded class information is stored in a memory space called the method area。 In addition to the class information, the method area also stores the runtime constant pool information, which may also include string literal and numeric constants (this part of the constant information is the memory mapping of the constant pool part in the class file)

    2. Class loading subsystem

    image.png

2.2 classloader role

2. Class loading subsystem

image.png
  • Class file exists on the local hard disk and can be understood as a template drawn on paper by the designer. Finally, the template is loaded into the JVM to instantiate n identical instances based on this file.
  • Class file is loaded into the JVM and is calledDNA metadata template, put onMethod area
  • stay. class file– > JVM – > eventually becomes a metadata template. In this process, a transport tool (class loader) plays the role of a courier.

Loading process of class 2.3

public class HelloLoader {
    public static void main(String[] args) {
        System.out.println("hi classloader");
    }
}

How is its loading process?

  • To execute the main () method (static method), you need to load the bearing class helloloader first
  • If the loading is successful, perform the linking, initialization and other operations, and then call the static method main in the helloloader class
  • If the loading fails, an exception is thrown
    2. Class loading subsystem

    image.png

    The complete flow chart is as follows:Load -- > link (verify -- > prepare -- > resolve -- > initialize

    2. Class loading subsystem

    image.png

2.3.1 loading

1. Get the binary byte stream defining this class through the fully qualified name of a class
2. Convert the static storage structure represented by this byte stream into the runtime data structure of the method area
3、Generate a Java. Net file representing this class in memory (method area) Lang.class object, as the access entry of various data of this class in the method area

Supplement: how to load class files

  • Load directly from local system
  • Get through the network, typical scenario: Web applet
  • Read from the zip package and become the basis of jar and war formats in the future
  • Dynamic agent technology is the most widely used in runtime computing generation
  • Generated by other files, typical scenario: JSP Application
  • Extract from a proprietary database Class file, relatively rare
  • Obtained from encrypted files, typical protection measures against decompilation of class files

2.3.2 linking

Verify
The purpose is to ensure that the information contained in the byte stream of the subclass file meets the requirements of the current virtual machine and ensure the correctness of the loaded class, it will not endanger the security of the virtual machine itself.
It mainly includes four kinds of verification, file format verification, metadata verification, bytecode verification and symbol reference verification.

2. Class loading subsystem

image.png

For example, the magic number used to judge the file type
Prepare
Allocate memory for class variables (static variables) and set the default initial value of such variables, i.e. zero value.

class demo{
  private static int i =5;  //  In the Prepare phase, I = 0 -- > in the initialization phase, I = 5
  final static int j = 6;  //  Final modified J = 6 in the prepare stage
}

Static modified with final is not included here, because final will be allocated during compilation and will be explicitly initialized in the preparation stage
Initialization will not be assigned to instance variables here,Class variables are assigned in the method areaInstance variables are allocated to the Java heap along with the object
Resolve
The process of converting a symbolic reference in a constant pool to a direct reference
In fact, the parsing operation is often accompanied by the JVM after initialization.
A symbol reference is a set of symbols that describe the referenced target. The literal form of symbol reference is clearly defined in the class file format of Java virtual machine specification. A direct reference is a pointer directly to the target, a relative offset, or a handle indirectly located to the target.
Parsing actions are mainly aimed at classes or interfaces, fields, class methods, interface methods, method types, etc. Corresponding to constant in constant pool_ Class_ info,CONSTANT_ Fieldref_ info、CONSTANT_ Methodref_ Info, etc.

2.3.3 initialization phase

  • The initialization phase is the execution of class constructor methods<clinit>()The process of.
  • This method does not need to be defined, it isJavac compilerAutomatically collect the assignment actions of all class variables in the class and the statements in the static code block. In other words, when we include static variables in our code, there will be<clinit>()method; If the static variable does not exist in the current class, its bytecode file will not exist<clinit>()
  • The instructions in the constructor method are executed in the order in which the statements appear in the source file.
  • <clinit>()Constructor different from class. Clinit is built-in, not defined by us (Association: the constructor is from the perspective of virtual machine)<init>()
  • If the class has a parent class, the JVM will guarantee the of the child class<clinit>()Before execution, the of the parent class<clinit>()The implementation has been completed.
  • The virtual machine must guarantee the of a class<clinit>()Method is locked synchronously under multithreading.

When the static variable is included in our code, there will be a < clinit > () method

public class AppTest {
    private static int num =1;
    static {
        num = 2;
    }
    public static void main(String[] args) {
        System.out.println(AppTest.num);
    }
}

View bytecode

2. Class loading subsystem

image.png

It can be seen that clinit is mainly a method to combine the initialization of static variables and static code blocks. First attach 1 to the static variable, and then assign 2 to the static variable.

When there is no static variable in our code, there is no < clinit > () method

public class AppTest {
    private int a = 2;
    public static void main(String[] args) {
        int b = 3;
    }
}
2. Class loading subsystem

image.png

The instructions in the constructor method are executed in the order in which the statements appear in the source file.

Look at another example. In the example just now, add a static variable

public class AppTest {
    private static int num =1;
    static {
        num = 2;
        number = 20;
    }

    private static int number = 10;
    public static void main(String[] args) {
        System.out.println(AppTest.num);
        System.out.println(AppTest.number);
    }
}

The output here is:
2
10

2. Class loading subsystem

image.png

The num here has been explained above. Now we can see how the number is assigned. In the preparation stage of linking, both the static variables num and number are instantiated as 0. Then in the initialization stage, when initializing the static variable, first execute the operation of number = 20, and then execute the operation of number = 10,Is executed in the order of the source filesSo the final result is 10.

The constructor is < init > ()

public class AppTest {
    private int a = 1;
    private static int c = 3;
    public AppTest(){
        a = 10;
        int d = 20;
    }
    public static void main(String[] args) {
        int b = 2;
    }
}

2. Class loading subsystem

image.png

You can see that what is executed in the init method is actually the part of the constructor in the code,Therefore, the constructor is < init > () from the perspective of virtual machine, because each class will have a constructor that is empty by default, this init method must exist in the virtual machine, unlike < clinit > ()

If the class has a parent class, the JVM will ensure that the < clinit > () of the parent class has been executed before the < clinit > () of the child class is executed

public class AppTest {
    static class Father{
        public static int A = 1;
        static {
            A = 2;
        }
    }
    static class Son extends Father{
        public static int B = A;
    }

    public static void main(String[] args) {
        System.out.println(Son.B); // 2
    }
}

Before son’s clinit is executed, father’s clinit has been executed, so a = 2, B = a, then B = 2\

The virtual machine must ensure that the < clinit > () method of a class is locked synchronously under multithreading

public class AppTest {

    public static void main(String[] args) {
        Runnable r = () -> {
            System. out. println(Thread.currentThread(). Getname() + "start");
            DeadThread deadThread = new DeadThread();
            System. out. println(Thread.currentThread(). Getname() + "end");
        };

        Thread t1 = new Thread(r,"t1");
        Thread t2 = new Thread(r,"t2");

        t1.start();
        t2.start();
    }
}

class DeadThread{
    static {
        if(true){
            System. out. println(Thread.currentThread(). Getname() + "initialize current class");
            while(true){

            }
        }
    }
}

2. Class loading subsystem

image.png

The program is stuck. Because deadthread is a static static code block, it is in<clinit>()Method, and this static code block is an endless loop. At this time, when two threads initialize deadthread at the same time, only one “initialize current class” is output, which indicates that the JVM executes the < clinit > () method synchronously in multiple threads

3. Class loader classification

  • The JVM supports two types of class loaders. RespectivelyBootstrap classloaderandUser defined classloader, where the boot class loader is written in C + +.
  • Conceptually, a custom class loader generally refers to a class loader customized by developers in a program, but it is not defined in the Java virtual machine specificationAll class loaders derived from the abstract class classloaderAre divided intoCustom class loader
  • No matter how the types of class loaders are divided, there are only three most common class loaders in the program, as shown below (the arrow here does not refer to inheritance)

    2. Class loading subsystem

    image.png

Why are extclassloader and appclassloader custom loaders

  • Specification definition: all class loaders derived from the abstract class classloader are divided into custom class loaders
    Since bootstrap is written in C + +, it cannot be found in the Java source code
    Appclassloader inheritance tree

    2. Class loading subsystem

    image.png

    Extclassloader inheritance tree

    2. Class loading subsystem

    image.png

    Get related class loader

public class ClassLoaderTest {
    public static void main(String[] args) {
        //Get system class loader
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);  // [email protected]

        //Get upper loader: expand class loader
        ClassLoader extClassLoader = systemClassLoader.getParent();
        System.out.println(extClassLoader);   // [email protected]

        //Get the upper loader of the extended class loader: boot class loader
        ClassLoader bootStrapClassLoader = extClassLoader.getParent();
        System.out.println(bootStrapClassLoader); // null

        //Gets the class loader that loads the current class
        //For users: the system class loader is used for loading by default
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        System.out.println(classLoader); // [email protected]

        //Gets the class loader of the string class
        //Java core class libraries are loaded using the bootstrap class loader
        ClassLoader classLoader1 = String.class.getClassLoader();
        System.out.println(classLoader1); // null
    }
}
  • We try to get the boot class loader, and the obtained value is null, which does not mean that the boot class loader does not exist, because the boot class loader is composed of C / C + + language, so we can’t get it
  • The value of the system class loader obtained twice is the same: sun misc. Launcher$ [email protected] , which indicates that the system class loader is globally unique

3.1 loader of virtual machine

3.1.1 boot loader (boot loader)

Boot classloader (boot classloader, bootstrap classloader)

  • This class loads and usesC / C + + languageImplemented, nested inside the JVM
  • It usedLoad the core library of Java(contents in java_home / JRE / lib / rt.jar, resources.jar or sun.boot.class.path path) to provide classes required by the JVM itself
  • Does not inherit from Java lang.ClassLoader, no parent loader
  • Load the extension class and application class loader and act as their parent class loader (when their father)
  • For security reasons, the bootstrap boot class loader only loads packages namedjava、javax、sunClass starting with

3.1.2 extended class loader

Extension classloader

  • Written in Java language, bysun.misc.Launcher$ExtClassLoaderrealization
  • Derived fromClassLoaderclass
  • The parent class loader is the boot class loader
  • fromjava.ext.dirsLoad the class library in the directory specified by the system attribute, or load the class library from the JRE / lib / ext subdirectory (extension directory) 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

3.1.3 system loader

Application class loader (system class loader, appclassloader)

  • Written in Java language, by sun misc. Launchersappclassloader implementation
  • Derived from classloader class
  • The parent class loader is an extension class loader
  • It is responsible for loading environment variablesclasspathorSystem properties Java class. pathClass library under the specified path
  • This class loader is the default class loader in the program. Generally speaking,Java application classes are loaded by it
  • adoptclassLoader.getSystemclassLoader( )Method to get the class loader

Code description

public class ClassLoaderTest1 {
    public static void main(String[] args) {
        System. out. Println ("********************************************");
        //Get the API that bootstrapclassloader can load
        URL[] urLs = Launcher.getBootstrapClassPath().getURLs();
        for (URL element : urLs) {
            System.out.println(element.toExternalForm());
        }
        //Select any class from the above path to see what its classloader is: boot classloader
        ClassLoader classLoader = Provider.class.getClassLoader();
        System.out.println(classLoader);

        System. out. Println ("**********************************************");
        String extDir = System.getProperty("java.ext.dirs");
        for(String path : extDir.split(";")){
            System.out.println(path);
        }
        //Select any class from the above path to see what its classloader is: extended classloader
        ClassLoader classLoader1 = CurveDB.class.getClassLoader();
        System.out.println(classLoader1);

    }
}

***********Start class loader*****************
file:/D:/OpenSource/jdk1.8/jre/lib/resources.jar
file:/D:/OpenSource/jdk1.8/jre/lib/rt.jar
file:/D:/OpenSource/jdk1.8/jre/lib/sunrsasign.jar
file:/D:/OpenSource/jdk1.8/jre/lib/jsse.jar
file:/D:/OpenSource/jdk1.8/jre/lib/jce.jar
file:/D:/OpenSource/jdk1.8/jre/lib/charsets.jar
file:/D:/OpenSource/jdk1.8/jre/lib/jfr.jar
file:/D:/OpenSource/jdk1.8/jre/classes
null
**************Extended class loader****************
D:\OpenSource\jdk1.8\jre\lib\ext
C:\windows\Sun\Java\lib\ext
[email protected]

Process finished with exit code 0

3.2 user defined class loader

Why do I need a custom class loader?

In the daily application development of Java, class loading is almost performed by the above three kinds of loaders. When necessary, we can also customize the class loader to customize the loading method of the class.

effect:

  • Isolate loading classes (prevent duplicate class names)
  • Modify how classes are loaded
  • Extended load source
  • Prevent source code leakage

How to customize the class loader?

  • Developers can inherit the abstract class java Lang. classloader class to implement its own class loader to meet some special needs
  • In jdk1 2 ago, when customizing the class loader, we always inherited the classloader class and overridden the loadclass () method to realize the custom class loading class, but in jdk1 After 2, users are no longer recommended to override the loadclass () method, but to write the custom class loading logic in the findclass () method
  • When writing a custom class loader, if there are no too complex requirements, you can directly inherit the uriclassloader class. In this way, you can avoid writing the findclass () method and its way to obtain the byte code stream, making the writing of the custom class loader more concise.

Code description

public class CustomClassLoader extends ClassLoader{
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try{
            byte[] result = getClassFromCustomPath(name);
            if(result == null){
                throw new FileNotFoundException();
            }else {
                return defineClass(name, result,0,result.length);
            }
        }catch (FileNotFoundException e){
            e.printStackTrace();
        }
        throw new ClassNotFoundException();
    }

    private byte[] getClassFromCustomPath(String name) {
        //Load the specified class from the custom path: Details
        //If the bytecode file of the specified path is encrypted, decryption is required in this method
        return null;
    }

    public static void main(String[] args) {
        CustomClassLoader customClassLoader = new CustomClassLoader();
        try {
            Class<?> clazz = Class.forName("one", true, customClassLoader);
            Object instance = clazz.newInstance();
            System.out.println(instance.getClass().getClassLoader());
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

3.3 about classloader

Classloader class, which is an abstract class. All subsequent class loaders inherit from classloader (excluding startup class loader)

Method name describe
getParent( ) Returns the superclass loader of this class loader
loadClass(String name) Load the class named name, and the returned result is Java Instance of lang.class class
findClass(String name) Find the class named name, and the returned result is Java Instance of lang.class class
findLoadedClass(String name) Find the loaded class named name, and the returned result is Java Instance of lang.class class
defineClass(String name,byte[ ] b,int len) Convert the contents of byte array B into a Java class, and the return result is Java Instance of lang.class class
resolveClass(Class<?> c) Connect to a Java class specified

Ways to get classloader

Ways to get classloader
Method 1: get the classloader of the current class
clazz.getClassLoader()
Method 2: get the classloader of the current thread context
Thread.currentThread().getContextClassLoader()
Method 3: get the classloader of the system
ClassLoader.getSystemClassLoader()
Method 4: get the classloader of the caller
DriverManager.getCallerClassLoader ()

Code description

public class ClassLoaderTest2 {
    public static void main(String[] args) {
        try{
            //Mode 1 boot class loader
            ClassLoader classLoader = Class.forName("java.lang.String").getClassLoader();
            System.out.println(classLoader);

            //Mode 2 system loader
            ClassLoader classLoader1 = Thread.currentThread().getContextClassLoader();
            System.out.println(classLoader1);

            //Mode 3 system loader
            ClassLoader classLoader2 = ClassLoader.getSystemClassLoader();
            System.out.println(classLoader2);

        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

4. Parental delegation mechanism

The Java virtual machine uses theLoad on demandThat is, when the class needs to be used, its class file will be loaded into memory to generate class objects. Moreover, when loading the class file of a class, the Java virtual machine adoptsParental delegation model, that is, the request is handled by the superior loader. It is a task delegation mode

  • If a class loader receives a class loading request, it will not load it first, but send the requestLoader delegated to superiorTo execute;
  • If the parent loader still has its parent loader, delegate further upward and recurse in turn, and the request will eventually arriveTop level boot class loader
  • If the parent loader can complete the class loading task, it will return successfully. If the parent loader cannot complete the loading task, the child loader will try to load it by itself. This is the two parent delegation mode
  • The parent class loader assigns tasks layer by layer. If the child class loader can load, this class will be loaded. If the loading task is assigned to the system class loader and this class cannot be loaded, an exception will be thrown

    2. Class loading subsystem

    image.png

Code description

Example 1:
Suppose someone accidentally creates a Java A string Lang class is created in the package

2. Class loading subsystem

image.png
public class String {
    static{
        System. out. Println ("custom string");
    }
}

Write the test class to see whether the final loading is the system string or the string we wrote ourselves. If we wrote it ourselves, we will print out the static code block in the initialization stage

public class StringTest {
    public static void main(String[] args) {
        java.lang.String s = new java.lang.String();
        System.out.println("hello,world");
    }
}

hello,world

Process finished with exit code 0
If there is no static code block in the final output, it indicates that it has not been loaded. From here, we can see the parental delegation mechanism. The current class is loaded by appclassloader, and it will be delegated upward to extclassloader, Java Lang.string does not belong to JRE / lib / ext package. It will also be delegated upward to bootstrapclassloader. Bootstrapclassloader finds this class in rt.jar and loads it. The subsequent classloader can be obtained directly, so there is no need to load.
Example 2:

public class String {
    static{
        System. out. Println ("custom string");
    }

    public static void main(String[] args) {
        System.out.println("hello");
    }
}
2. Class loading subsystem

image.png

Reason: due to the parental delegation mechanism, our string class is loaded by the boot class loader, which has no main method, so an error will be reported
Example 3:
The SPI interface is loaded by the boot class loader. The specific implementation class of the interface is loaded by the thread context class loader, and the thread context class loader is the system class loader. Therefore, when loading, we will first carry out parent delegation, load the SPI core class in the boot class loader, then load the SPI interface, and finally implement class JDBC through the system class loader in reverse delegation Loading of jar

2. Class loading subsystem

image.png

Example 4:

package java.lang;

public class ShkStart {
    public static void main(String[] args) {
        System.out.println("hello!");
    }
}
2. Class loading subsystem

image.png

For protection mechanism, Java Lang package does not allow us to customize classes

4.3 advantages of parental delegation

  • Avoid repeated loading of classes
  • Protect the program security and prevent the core API from being tampered with at will
    • Custom class: Java Lang.string is not called
    • Custom class: Java Lang. shkstart (error: prevent the creation of classes starting with Java. Lang)

5 sandbox security mechanism

When customizing the string class: when loading the custom string class, the boot class loader will be used first. During the loading process, the boot class loader will first load the JDK’s own file (java.lang.string.class in rt.jar package). The error message says that there is no main method because the string class in rt.jar package is loaded.
This can ensure the protection of Java core source code, which isSandbox security mechanism

6 others

How to determine whether two class objects are the same?

In the JVM, there are two necessary conditions to indicate whether two class objects are the same class:

  • Class integrityClass names must be consistent, includingPackage name
  • Load this classClassloader (referring to classloader instance object) must be the same

In other words, in the JVM, even if the two class objects (class objects) come from the same class file and are loaded by the same virtual machine, as long as the classloader instance objects that load them are different, the two class objects are not equal

Reference to class loader

  • The JVM must know whether a type is loaded by the boot loader or the user class loader
  • If a type is loaded by the user class loader, the JVM willA reference to the class loaderSaved as part of type information inMethod areain
  • When resolving a reference from one type to another, the JVM needs to ensure that the class loaders of the two types are the same

Active and passive use of classes

Java programs use classes in the following ways:Active useandPassive use
Active use can be divided into seven cases:

  • Create an instance of a class
  • Access or assign a value to a static variable of a class or interface
  • Calling a static method of a class
  • Reflection (for example: class. Forname (“CN. SXT. Test”))
  • Initializes a subclass of a class
  • The class marked as the startup class when the Java virtual machine starts
  • JDK7 started to provide dynamic language support: Java lang.invoke. Parsing result of methodhandle instance ref_ getStatic、REF putStatic、REF_ If the class corresponding to the invokestatic handle is not initialized, it will be initialized

In addition to the above seven cases, other ways of using java classes are regarded as the of classesPassive useWill not lead to class initialization, that is, the initialization phase will not be executed (clinit () method and init () method will not be called)