JVM——Class Loading Mechanism (2)

3. The whole process of class loading

The whole process of class loading in the java virtual machine, that is , the specific actions performed in the five stages of loading, verification, preparation, parsing and initialization .

1. Load

During the loading phase, the virtual machine needs to complete the following 3 things:

① Get the binary stream of this class through the fully qualified name of a class. - The most controllable stage for developers (usually read from jar packages)

② Convert the static storage structure represented by this byte stream into the runtime data structure of the method area.

③ Generate a java.lang.Class object representing this class in memory as the access entry for the various data in the method area. - This is a special object, which is stored in the method area

The first stage of loading needs to be described in more detail here:

Compared with other stages of the class loading process, the loading stage of a non-array class (to be precise, the stage of obtaining the binary byte stream of the class in the loading stage) is the most controllable by the developer, because the loading stage can either It is done using the bootstrap class loader provided by the system, or it can be done by a user-defined class loader. Developers can control how the byte stream is obtained by defining their own class loader (that is, overriding the loadClass() method of a class loader).

For array classes, the situation is different, the array class itself is not created by the class loader, it is created directly by the java virtual machine. However, the array class is still closely related to the class loader, because the element type of the array class (Element Type, which refers to the type of the array without all dimensions) is ultimately created by the class loader. The process of creating an array class is Follow these rules:

① If the component type of the array is a reference type, then recursively use the loading process defined in this section to load the component type, and the array C will be marked on the class namespace of the class loader that loads the component type (this is very Important, as we'll see later, a class must work with the class loader to determine uniqueness).

② If the component type of the array is not a reference type (such as an int[] array), the java virtual machine will mark the array c as associated with the bootstrap class loader.

③ The visibility of the array class is consistent with the visibility of its component type. If the component type is not a reference type, the visibility of the array will be public by default.

 

The loading phase is not over yet, and the connection phase may have already started. Although the two phases may overlap, the start times of the two phases still maintain a fixed sequence .

2. Verify

The purpose of this stage is to ensure that the information contained in the byte stream of the Class file meets the requirements of the current virtual machine and will not endanger the security of the virtual machine itself.

Why is there a verification phase?

The Java language itself is a relatively safe language (still relative to C/C++), and pure Java code cannot do things such as accessing data outside the bounds of an array, converting an object to it and implementing it, jumping to a non-existent line of code and things like that, the compiler will refuse to compile if it does.

But as mentioned earlier, Class files are not necessarily compiled from Java source code, and even Class files can be written directly. At the bytecode level, the above-mentioned things that cannot be done by the Java language are achievable. Therefore, there must be a verification stage to prevent the system from crashing due to the input of harmful byte streams. Therefore, verification is an important part of the virtual machine's own protection. Work.

If it is verified that the input byte stream does not conform to the Class file format, the virtual machine should throw a java.lang.VerifyError exception or its subclass exception.

The verification phase generally completes the following four phases of verification actions:

①File format verification : Verify whether the byte stream conforms to the specification of the Class file format and can be processed by the current version of the virtual machine.

The main purpose is to ensure that the input byte stream can be correctly parsed and stored in the method area, and the format meets the requirements for describing a Java type information. Only after passing the verification of this stage, the byte stream will enter the method area of ​​the memory for storage, so the next three stages are based on the storage structure of the method area, and the byte stream will not be directly manipulated.

②Metadata verification : Semantic analysis is performed on the information described by the bytecode to ensure that the information described meets the requirements of the Java language specification.

Bytecode verification: The main purpose is to determine whether the program semantics is legal and logical through data flow and control flow analysis. After verifying the data types in the metadata in the second phase, this phase will verify and analyze the accumulated method body to ensure that the Beijiao Tears method will not cause events that endanger the security of the virtual machine during operation.

Symbolic reference verification : The last stage of verification occurs when the virtual machine converts a symbolic reference to a direct reference. This conversion action will occur in the third stage of the connection - the parsing stage. Symbolic reference verification can be seen as matching information other than the class itself (various symbolic references in the constant pool).

The purpose is to ensure that the parsing action can be performed correctly. If the symbol reference verification cannot be passed, a subclass of java.lang.IncompatibloeClassChangeError will be thrown.

3. Prepare

The prepare phase is the phase where memory is formally allocated for class variables and the initial value (zero value) of the class variable is set. The memory used by these variables will be allocated in the method area.

Two points need to be emphasized: 1. At this time, only class variables are allocated for memory allocation, not instance variables. Instance variables will be allocated in the Java heap along with the object when the object is instantiated. ②The initial value mentioned here is "normally" the zero value of the data type. There are also some special cases. If the field attribute table of the class field has the ConstantValue attribute, then the variable value in the preparation stage will be initialized to the value specified by the ConstantValue attribute, such as the code

public static final int value=123;

Because of the final, javac will generate the ConstantValue property for the value at compile time, and the virtual machine will assign the value to 123 according to the ConstantValue in the preparation stage.

4. Analysis

The resolution phase is the process by which the virtual machine replaces symbolic references in the constant pool with direct references.

Symbolic Reference : A symbolic reference can use any form of literal, as long as it can be used to locate the target without ambiguity. Symbolic references have nothing to do with the memory layout implemented by the virtual machine, and the target of the reference is not necessarily loaded into memory . The memory layout of various virtual machine implementations can be different, but the symbolic references they can accept must be consistent , because the literal form of symbolic references is clearly defined in the class file format of the Java virtual machine specification.

Direct Reference : A direct reference can be a pointer directly to the target, a relative offset or a handle that can be located indirectly to the target. The direct reference is related to the memory layout implemented by the virtual machine. The direct reference translated from the same symbolic reference on different virtual machine instances is generally different. If there is a direct reference, the target of the reference must already exist in memory.

The parsing action is mainly performed on 7 types of symbolic references, namely class or interface, field, class method, interface method, method type, method handle and call site qualifier.

5. Initialization

The class initialization stage is the last step of the class loading process. In the previous class loading process, except that the user application can participate in the loading stage through the custom class loader, the rest of the actions are completely dominated and controlled by the virtual machine. At the initialization stage, the Java program code (or bytecode) defined in the class is actually executed.

The initialization phase is the process of executing the class constructor <clinit>() method.

① The <clinit>() method is composed of the assignment actions of all the class variables in the class and the statements in the static statement block, which are automatically collected by the compiler.

It should be noted that in the static statement block, only the variables defined before the static statement block can be accessed, and the variables defined after it can be assigned values ​​in the preceding static statement block, but cannot be accessed.

public class Test{
    static{
        i=0;                  //给变量赋值可以正常编译通过
        System.out.print(i);  //这句编译器会提示“非法向前引用”
    }
    static int i=1;
}

② The <clinit>() method is different from the class constructor (or the instance constructor <init>() method), it does not need to explicitly call the parent class constructor, and the virtual machine guarantees that the subclass's <clinit>() Before the method is executed, the <clinit>() method of the parent class has been executed. So the class of the first <clinit>() method to be executed must be java.lang.Object

③ Since the <clinit>() method of the parent class is executed first, it means that the static statement block defined in the parent class has priority over the variable assignment operation of the child class.

④ The <clinit>() method is not necessary for a class or interface. If there is no static and too fast in a class, and there is no assignment operation to variables, the compiler may not generate the <clinit>() method for this class.

⑤ The static statement block cannot be used in the interface, but there is still the assignment operation of variable initialization. It should be noted that executing the <clinit>() method of the interface does not need to execute the <clinit>() method of the parent interface first. The parent interface is initialized only when the variables defined in the parent interface are used. In addition, the implementation class of the interface will not execute the <clinit>() method of the interface when it is initialized.

⑥ The virtual machine ensures that the <clinit>() method of a class is correctly locked and synchronized in a multi-threaded environment. If multiple threads initialize a class at the same time, only one thread will execute the <clinit>() of this class. )method.

Guess you like

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