[JVM Series] Detailed Explanation of Java Class Loading Mechanism


Classes are dynamically loaded when they are first used at runtime, rather than all classes being loaded at once. Because if it is loaded at one time, it will take up a lot of memory.

1. The life cycle of a class

11.png
It includes the following 7 stages:

  • Loading
  • Verification
  • Preparation
  • Resolution
  • Initialization
  • Using
  • Unloading

2. Class loading process

Contains five stages of loading, verification, preparation, parsing and initialization.

load

The loading process does three things:

  • Get the binary byte stream defining the class by its fully qualified name.
  • Convert the static storage structure represented by the byte stream into the runtime storage structure of the method area.
  • Generate a Class object representing this class in the memory as the access entry for various data of this class in the method area.

The binary byte stream can be obtained from the following ways:

  • Read from a ZIP package, which forms the basis for JAR, EAR, WAR formats.
  • Obtained from the network, the most typical application is Applet.
  • Run-time computation generates, for example, dynamic proxy technology, the binary byte stream of the proxy class using ProxyGenerator.generateProxyClass in java.lang.reflect.Proxy.
  • It is generated by other files, for example, the corresponding Class class is generated by a JSP file.

verify

The JVM will verify the binary byte stream at this stage, and only those that conform to the JVM bytecode specification can be correctly executed by the JVM. This stage is an important barrier to ensure the safety of JVM, the following are some main checks.

  • Make sure the binary byte stream format is as expected (e.g. cafe benestarts ).
  • Whether all methods are subject to the qualification of access control keywords.
  • Whether the number and type of parameters of the method call are correct.
  • Make sure variables are properly initialized before use.
  • Checks that a variable is assigned a value of the proper type.

Prepare

staticAt this stage, the JVM will allocate memory and initialize class variables (also known as static variables, keyword-modified) (corresponding to the default initial value of the data type, such as 0, 0L, null, false, etc.).

Memory for instance variables is not allocated at this point because instance variables are created on the Java heap along with object instantiation. And at this time, the class variable is assigned a value of zero, that is, the zero value of the int type is 0, and the zero value of the reference type is null, instead of the value displayed in the code.

analyze

This phase converts symbolic references in the constant pool to direct references.

Literal and symbolic references are stored in the constant pool in the class file, and symbolic references include fully qualified names of classes and interfaces, and names and descriptors of fields and methods.

When the JVM is dynamically linked, it needs to convert these symbolic references into direct references to store memory usage.

initialization

This phase is the last step in the class loading process. In the preparation phase, the class variables have been assigned default initial values, and in the initialization phase, the class variables will be assigned the values ​​​​that the code expects to assign. In other words, the initialization phase is the process of executing class constructor methods.

3. Class loading timing

  • The four bytecode instructions new, getstatic, putstatic, and invokestatic initialize the class (that is, initialize the class when instantiating objects, reading and writing static objects, and calling static methods);
  • When using the reflection mechanism to call a class, initialize the class;
  • To initialize a class, if its parent class is not initialized, initialize its parent class first;
  • When the virtual machine starts, initialize an execution main class;
  • When using the dynamic language support of JDK1.7, if the analysis result of the MethodHandle instance is the method handle of REF_getstatic, REF_putstatic, REF_invokestatic (ie: read and write static objects or call static methods), initialize the corresponding class of the handle.

Four, class loader classification

When it comes to class loading, we have to talk about the order of class loading and class loaders.

There are about four class loaders in Java, namely: Bootstrap ClassLoader, Extension ClassLoader, System ClassLoader, Custom ClassLoader, in order It belongs to the inheritance relationship (note that the inheritance here is not the extends in the Java class)

classloader2.jpg

  1. Bootstrap ClassLoader: Mainly responsible for loading class libraries stored under Java_Home/jre/lib, or under the path specified by the -Xbootclasspath parameter, and recognized by the virtual machine (such as rt.jar, all java .* are loaded by Bootstrap ClassLoader), and the bootstrap class loader cannot be directly referenced by Java programs.
  2. Extension ClassLoader (Extension ClassLoader): The main loader is implemented by sun.misc.Launcher$ExtClassLoader, which is responsible for loading the Java_Home/jre/lib/ext directory, or the path specified by the java.ext.dirs system variable For all class libraries (such as classes beginning with javax.*), developers can directly use the extended class loader.
  3. System ClassLoader (System ClassLoader): The main responsible loader is implemented by sun.misc.Launcher$AppClassLoader, which is responsible for loading the classes specified by the user class path (ClassPath). Developers can directly use this class loader, if the application The program has not customized its own class loader. Generally, this is the default class loader in the program.
  4. Custom class loader (Custom ClassLoader: Class loader developed by yourself.

5. The principle of parental delegation

When the class loader loads the class file, it follows the principle of parental delegation, which means that the parent loader performs the loading action first, and only when the parent loader does not load the class file, the child class loader loads it. This mechanism guarantees the security of Java API very well, so that the code of JDK will not be tampered with.

6. JVM instructions in Java bytecode files

1. Create a Java source file Test02.java, and complete simple logic operations in the main method, as shown below.

public class Test02 {
    
    
    public static void main(String[] args) {
    
    
        int i = 5;
        int j = 10;
        int k = i + j;
        System.out.println(k);
    }
}

2. Compile HelloWorld.java through the javac command on the terminal.

javac Test02.java

3. Decompile it into JVM instructions that we can understand, here we use the javap -c command to complete.

javap -c Test02.class

4. The JVM instructions after decompilation are as follows.

1    Compiled from "Test02.java"
2    public class org.example.jvm.Test02 {
    
    
3     public org.example.jvm.Test02();
4        Code:
5           0: aload_0
6           1: invokespecial #1                  // Method java/lang/Object."<init>":()V
7           4: return
8
9      public static void main(java.lang.String[]);
10        Code:
11           0: iconst_5
12           1: istore_1
13           2: bipush        10
14           4: istore_2
15           5: iload_1
16           6: iload_2
17           7: iadd
18           8: istore_3
19           9: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
20          12: iload_3
21          13: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
22          16: return
    }

Explain the above JVM instructions:

Line 1 indicates the current bytecode file to compile from Test02.java.

Line 3 means Test02calling the no-argument constructor of to instantiate the current object.

Lines 4 to 7 represent the execution flow of the parameterless constructor.

Line 5 means to push this into the operand stack.

Line 6 means to call the no-argument construction of the parent class Object of Test02. We know that each object will instantiate its parent class object by default when it is instantiated, and call the no-argument construction of the parent class by default.

Line 7 return indicates that the execution of the construction method is completed.

Lines 10 to 22 represent the execution flow of the main method.

Line 11 indicates that the constant 5 is pushed onto the operand stack.

Line 12 means to take out the top element of the operand stack, namely 5, and then save it to the first position of the local variable table, namely the variable i.

Line 13 means pushing the constant 10 onto the operand stack.

Line 14 means to take out the top element of the operand stack, namely 10, and then save it to the second position of the local variable table, namely the variable j.

Line 15 means to push the first variable (i) of the local variable table into the operand stack.

Line 16 means to push the second variable (j) of the local variable table into the operand stack.

Line 17 means taking out the first two values ​​in the operand stack and adding them, and pushing the result to the top of the operand stack.

Line 18 means to take out the top element of the operand stack and save it to the third position of the local variable table, namely the variable k.

Line 19 reads the static instance PrintStream.

Line 20 means to push the third variable (k) of the local variable table into the operand stack.

Line 21 indicates calling the println method of PrintStream to output the top element of the operand stack (variable k).

Return on line 22 indicates that the main method is executed.

Guess you like

Origin blog.csdn.net/jiang_wang01/article/details/131173241