Deep understanding of jvm-class loading process

This article is for reading notes.I personally think that this part is very important compared to garbage collection, so I want to keep a note

1. The life cycle of a class

Load -> Authentication -> Prepare -> parse -> Initialization -> Use -> Uninstall
Insert picture description here
points:

  • The sequence of the five stages of loading, verification, preparation, initialization and unloading is determined
  • The parsing phase can start after the initialization phase in some cases
  • The actual "loading" of the first stage of the class loading process is not clearly defined
  • But in six cases, the class must be "initialized" immediately:
    1. When encountering the four bytecode instructions new, getstatic, putstatic, or invokestatic, if the type has not been initialized, you need to trigger its initialization phase first.

The trigger conditions of these four instructions are:

  • When using the new keyword to instantiate an object.
  • Read or set a type of static field
  • When calling a static method of a class

2. Reflect call, initialize if not initialized
3. Main method execution main class
4. Reflect method handle resolves to REF_getStatic, REF_putStatic, REF_invokeStatic, REF_newInvokeSpecial corresponding class has not been initialized
5. Interface method modified by default keyword is The implemented class is initialized first
. 6. When the class is initialized, if the parent class is not initialized, the initialization of the parent class is triggered.
Only these six scenarios represent active references to a type

Implementation case:

  • Referencing static fields defined in the parent class through its subclasses will only trigger the initialization of the parent class and not the subclasses.
  • Constants will be added to the constant pool of the class at compile time.In essence, there is no direct reference to the class that defines the constant, so it will not trigger initialization
  • The array will not trigger the initialization of the class. The reason is that this type of virtual machine automatically generates a subclass that directly inherits from java.lang.Object. The creation action is triggered by the bytecode instruction newarray.
    When considering whether a class is initialized, consider whether it is passively referenced; that is, class initialization is lazy.
    There are materials to test here. I list it and it will not cause initialization.
  • Class object
  • LoadClass method of class loading
  • When the second parameter of Class.forname () is false
  • The interface also has an initialization process, which is almost the same as the class

Personal understanding of class initialization:
In fact, it has been mentioned above that class initialization is lazy, which can save memory space.Of course, some programs emphasize a preheating process.If we preheat it all at the beginning, it will be No need to initialize

Example:
Insert picture description here
Test 1:
Insert picture description here
Insert picture description here
Test 2:
Insert picture description here
Insert picture description here
How can I trigger the initialization of MM?
Insert picture description here
Then execute the above code:
Insert picture description here
This leads to a conclusion: only basic types and String can be used as direct references to their own constant pool.
Other reference types can be used as final classes. The initialization of this class is triggered, that is, <cinit>, and the main work of <cinit> is to initialize the member variables defined in the class.

2. The process of class loading

2.1 "Loading" phase

Please understand that the "Loading" stage is a stage in the entire "Class Loading" process

1) Obtain the binary byte stream that defines this class by its fully qualified name.
2) Convert the static storage structure represented by this byte stream into the runtime data structure of the method area. 3) Generate a java.lang.Class object representing this class in memory as an access point to various data of this class in the method area .

important point:

  • The array class itself is not created by the class loader, but the element type of the array class is still loaded by the class loader
  • The loading phase has not been completed, the connection phase may have begun

2.2 Verification phase

The purpose is to ensure that the information contained in the byte stream of the Class file meets all the constraints of the "Java Virtual Machine Specification", the guarantee stage of the security of the Java language

1. File format verification

  • Whether it starts with the magic number 0xCAFEBABE.
  • Whether the major and minor version numbers are within the range accepted by the current Java virtual machine.
  • Are there any unsupported constant types in the constants of the constant pool (check the constant tag flag).
  • Is there a constant that points to a non-existent constant or does not match the type in various index values ​​that point to a constant?
  • Is there any data in CONSTANT_Utf8_info constant that does not conform to UTF-8 encoding?
  • Whether there are deleted or additional other information in each part of the Class file and the file itself.
  • …and many more

2. Metadata verification (semantic verification of class metadata information)

  • Does this class have a parent class (except java.lang.Object, all classes should have a parent class).
  • Whether the parent class of this class inherits a class that is not allowed to be inherited (a class modified by final).
    If the class is not an abstract class, whether all methods required by its parent class or interface are implemented.
  • Whether the fields and methods in the class conflict with the parent class (such as overwriting the final field of the parent class, or there is a method overload that does not meet the rules, such as the method parameters are consistent, but the return value type is different, etc.)
  • …and many more

3. Bytecode verification

  • Ensure that the data type and instruction code sequence of the operand stack can work together at any time
  • Ensure that any jump instruction will not jump to the bytecode instruction outside the method body.
  • · Ensure that the type conversion in the method body is always valid, for example, you can assign a subclass object to the parent class data type, which is safe, but assign the parent class object to the subclass data type, and even assign the object to A data type that has no inheritance relationship and is completely irrelevant is dangerous and illegal.
  • …and many more

4. Symbol reference verification

  • Whether the fully qualified name described by the character string in the symbol reference can find the corresponding class.
  • Whether there are methods and fields described by the method's field descriptor and simple name in the specified class.
  • Whether the accessibility (private, protected, public,) of the class, field, method in the symbol reference can be accessed by the current class.

2.3 Preparation stage

The preparation stage is the stage of officially allocating memory for the variables defined in the class (ie static variables, variables modified by static) and setting the initial value of the class variables

Note:
Memory allocation only includes class variables, not instance variables. Instance variables will be allocated in the Java heap along with the object when the object is instantiated.
Examples:

public static int value = 123;

The initial value of the variable value after the preparation phase is 0 instead of 123, because no Java method has been executed at this time, and the putstatic instruction with the value assigned to 123 is the program is compiled and stored in the class constructor () method , So the action of assigning the value to 123 will not be executed until the initialization stage of the class.
But for:

public static final int value = 123;

If the ConstantValue attribute exists in the field attribute table of the class field, the variable value will be initialized to the initial value specified by the ConstantValue attribute during the preparation stage

2.4 Resolution stage

The resolution phase is the process of replacing the symbol references in the constant pool with direct references by the Java virtual machine

To clarify the concept:
symbol reference: symbol reference describes the referenced target with a set of symbols. The symbol can be any form of literal, as long as it can be used to locate the target without ambiguity.

Direct reference: A direct reference is a pointer that can directly point to the target, a relative offset, or a handle that can be indirectly located to the target.
1. Class or interface analysis
2. Field analysis
3. Method analysis

2.5 Initialization phase

It was not until the initialization phase that the Java virtual machine actually started to execute the Java program code written in the class, and handed over the control to the application.
The initialization phase is the process of executing the class constructor () method.

The <clinit> () method is generated by the compiler automatically collecting the assignment actions of all class variables in the class and the statements in the static statement block (static {} block). The order in which the compiler collects the statements is in the source file The order of occurrence determines that only the variables defined before the static statement block can be accessed in the static statement block, and the variables defined after it can be assigned but cannot be accessed in the previous static statement block

The Java Virtual Opportunity ensures that the parent class's <clinit> () method has been executed before the child class's <clinit> () method is executed.

The <clinit> () method is not necessary for a class or interface. If there is no static statement block or assignment of variables to a class, the compiler may not generate the <clinit> () method for this class.
The <clinit> () method of the execution interface does not need to execute the <clinit> () method of the parent interface first, because the parent interface will be initialized only when the variables defined in the parent interface are used.

The Java virtual machine must ensure that a class's <clinit> () method is properly locked and synchronized in a multi-threaded environment

3. Class loader

The Java virtual machine design team intends to put the action of “obtaining a binary byte stream describing a class by its fully qualified name” during the class loading phase outside the Java virtual machine to allow the application to decide how to go. Get the required class. The code that implements this action is called the "Class Loader".

For any class, the class loader that loads it and the class itself must jointly establish its uniqueness in the Java virtual machine . Each class loader has an independent class namespace.

That is, whether two classes are "equal" is meaningful only if the two classes are loaded by the same class loader , otherwise, even if the two classes are derived from the same Class file, they are used by the same Java Virtual machine loading, as long as the class loader that loads them is different, those two classes must be unequal.

If there is a class object loaded by two class loaders, and the corresponding object is generated, then when the type check of the object belongs, the result is false

3.1 Parent delegation model

Category:
Bootstrap Class Loader (BootstrapClassLoader), this class loader is implemented in C ++ language and is part of the virtual machine itself;
all other class loaders, these class loaders are implemented by the Java language and exist independently outside the virtual machine And all inherited from the abstract class java.lang.ClassLoader.

Start class loader

Responsible for loading and storing in the <JAVA_HOME> \ lib directory, or stored in the path specified by the -Xbootclasspath parameter, and can be recognized by the Java virtual machine (identified by the file name, such as rt.jar, tools.jar, the name does not match The class library will not be loaded even if it is placed in the lib directory) The class library is loaded into the memory of the virtual machine.
The startup class loader cannot be directly referenced by the Java program. When writing a custom class loader, if you need to delegate the load request to the boot class loader for processing, you can directly use null instead.
Insert picture description here

Extension class loader

This class loader is implemented in the form of Java code in the class sun.misc.Launcher $ ExtClassLoader. It is responsible for loading all the libraries in the <JAVA_HOME> \ lib \ ext directory or the path specified by the java.ext.dirs system variable.

Application class loader

This class loader is implemented by sun.misc.Launcher $ AppClassLoader. Because the application class loader is the return value of the getSystem-ClassLoader () method in the ClassLoader class, it is also called the "system class loader" in some cases. It is responsible for loading all the class libraries on the user classpath (ClassPath), and developers can also use this classloader directly in the code. If you have not customized your own class loader in the application, in general this is the default class loader in the program.

Parent delegation model

Insert picture description here
The parent delegation model requires that in addition to the top-level startup class loader, the rest of the class loader should have its own parent class loader.
However, the parent-child relationship between class loaders is generally not implemented in an inheritance (Inheritance) relationship, but usually uses a composition (Composition) relationship to reuse the code of the parent loader.

Work process:
If a class loader loads the class received a request, it first does not own to try to load this class , and that this request is delegated to the parent class loader to complete , each level of the class loader is true , So all load requests should eventually be transferred to the top-level startup class loader. Only when the parent loader reports that it cannot complete the load request (the required class is not found in its search scope), the child loader It will try to complete the loading by itself.

Benefits:
An obvious benefit is that classes in Java have a hierarchical relationship with priority along with their class loaders.

For example, class java.lang.Object, which is stored in rt.jar, no matter which class loader wants to load this class, it is ultimately delegated to the startup class loader at the top of the model to load, so the Object class is in the program In all kinds of class loader environments, the same class can be guaranteed.

If you let other class loaders load it, the final type determination will definitely be confusing

Code:
Insert picture description here

Destroy parental delegation

loadclass-> findclass
JNDI service-> The basic type needs to be called back to the user's code
Hot deployment-> When a class loading request is received, scan the class and load it

Additional: different classloaders load the same class, and finally getclass equals to judge the false case

 public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        ClassLoader myClassLoader = new ClassLoader() {
            @Override
            public Class<?> loadClass(String name) throws ClassNotFoundException {
                try {
                    String filename = name.substring(name.lastIndexOf(".") + 1) + ".class";
                    InputStream is = getClass().getResourceAsStream(filename);
                    if (is == null) {
                        return super.loadClass(name);
                    }
                    byte[] b = new byte[is.available()];
                    is.read(b);
                    return defineClass(name, b, 0, b.length);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }
        };

        Class<?> aClass = myClassLoader.loadClass("com.lyq.boot.lexicalanalysis.A");
        Object o =  aClass.newInstance();
        A a = new A();
        System.out.println(o.getClass().equals(a.getClass()));
    }

Context class loader

That is, the class loaded by the parent class loader needs to be loaded by the
child class . The child class loader retains the reference of the parent class loader. But what if the class loaded by the parent class loader needs to access the class loaded by the child class loader? The most classic scenario is JDBC loading.

JDBC is a standard interface set by Java to access the database. It is included in the Java basic class library and loaded by the root class loader. The implementation libraries of various database vendors are introduced as third-party dependencies. This part of the implementation libraries is loaded by the application class loader.

Code to get Mysql connection:

// Load the driver

Class.forName("com.mysql.jdbc.Driver");

//Connect to the database

Connection conn = DriverManager.getConnection(url, user, password);

DriverManager is loaded by the startup class loader. The database driver (com.mysql.jdbc.Driver) used by it is loaded by the application class loader. This is the typical class loaded by the parent class loader and accessed by the subclass The class loaded by the device.

For the realization of this process, see the source code of DriverManager class:

//建立数据库连接底层方法
private static Connection getConnection(
        String url, java.util.Properties info, Class<?> caller) throws SQLException {
    //获取调用者的类加载器
    ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
    synchronized(DriverManager.class) {
        //由启动类加载器加载的类,该值为null,使用上下文类加载器
        if (callerCL == null) {
            callerCL = Thread.currentThread().getContextClassLoader();
        }
    }
```java
    //...

    for(DriverInfo aDriver : registeredDrivers) {
        //使用上下文类加载器去加载驱动
        if(isDriverAllowed(aDriver.driver, callerCL)) {
            try {
                //加载成功,则进行连接
                Connection con = aDriver.driver.connect(url, info);
                //...
            } catch (SQLException ex) {
                if (reason == null) {
                    reason = ex;
                }
            }
        } 
        //...
    }
}
在上面的代码中留意改行代码:

```java
callerCL = Thread.currentThread().getContextClassLoader();

This line of code gets the ContextClassLoader from the current thread, and where is the ContextClassLoader set? It is set in the Launcher source code above:

// Set the context class loader
Thread.currentThread (). SetContextClassLoader (this.loader); In
this way, the so-called context class loader is essentially an application class loader. Therefore, the context class loader is just a concept proposed to solve the reverse access of the class. It is not a brand new class loader. It is essentially an application class loader.

Published 37 original articles · praised 6 · visits 4641

Guess you like

Origin blog.csdn.net/littlewhitevg/article/details/105521641