JVM class loading mechanism (2): detailed description of class loading

In the previous section, we explained the five stages of JVM class loading, mainly loading, verification, preparation, parsing and initialization.

The loading phase mainly accomplishes 3 things:

1. Obtain the binary byte stream that defines this class through the fully qualified name of the 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.lang.Class object representing this class in memory as the access entry for various data of this class in the method area.

It should be noted that the loading and the part of the connection stage are crossed, and the loading may not end, and the connection may have already started.

The main steps in the verification phase:

 1. File format verification, which is mainly based on byte stream operations

 2. Metadata validation. Operate on the storage structure of the method area

 3. Bytecode verification   operates on the storage structure of the method area

 4. Symbolic reference verification  operates on the storage structure of the method area

The main steps in the preparation phase:

The preparation phase is mainly to allocate memory and set the initial values ​​of class variables, which will be allocated in the local method area. The setting initial value here refers to the 0 value of the type, for the following code

public static int value=123;

The initial value here is 0 instead of 123, and assigning value to the value 123 is in the initialization phase of the class.

For static variables with final modification, when the value is specified at the beginning, it is set to the value specified in the code, assuming

public static final int value=123;

The value here will be initially set to 123.

Parsing phase:

This phase is the process of replacing symbolic references to the constant pool with direct applications.

Analysis of classes and interfaces

field parsing

class method parsing

Analysis of interface methods

initialization:

The static statement block can only access the variables defined before the static statement block, the variables defined after it, the static statement in front can be copied, but cannot be accessed

package cn.edu.hust.jvm;

public class MainTest {
    static
    {
        i=0;
        System.out.println(i);
    }
    public static int i=1;
    
    public static void main(String[] args)
    {
//        System.out.println(SubClass.value);
//        SubClass[] tt=new SubClass[10];
        System.out.println(Constant.s);
    }
}

The above statement, the IDE has started to report an error.

In the initialization phase, the process of <clinit>() fanff of the class constructor is executed. This method is generated by the compiler automatically collecting the assignment actions of all class variables in the class and combining the statements in the static statement block.

The method of <clinit> is different from the constructor of the class. It does not need to call the constructor of the parent class first. The virtual machine ensures that the method of the parent class has been executed before the <clinit> method of the subclass is executed. Therefore, in the virtual machine, the first execution must be the <clinit> method of the object class.

Here is a small example of a test:

package cn.edu.hust.jvm;

public class SuperClass {
    public static int value=123;
    static
    {
        value=10;
        System.out.println("The parent class is initialized");
    }

}

package cn.edu.hust.jvm;

public class SubClass extends SuperClass{
    public static int t=value;
    static
    {
        System.out.println("Subclass initialization");
    }
}

package cn.edu.hust.jvm;

public class MainTest {


    public static void main(String[] args)
    {
//        System.out.println(SubClass.value);
//        SubClass[] tt=new SubClass[10];
        System.out.println(SubClass.t);
    }
}

The promised result is


It can be seen that the <clinit>() method of the parent class is executed, and then the subclass.

If a class has no static block, the class can have no <clinit> method.

An interface's <clinit>() does not need to execute the parent interface's <clinit> method first.

For the <clinit>() method, it can be correctly locked and synchronized in a multi-threaded environment, with only one thread to initialize a class.




Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325722009&siteId=291194637