It is generally divided into three steps of loading, connection and initialization, and connection can be divided into three steps of verification, preparation and analysis, so the process is roughly as follows:
Loading stage
The first step is to generate a binary bytecode stream based on the fully qualified name of the class. The second step is to convert the static storage structure in the class file to the runtime data storage structure based on the binary bytecode stream (e.g., static constant pool — > runtime constant pool). Third, generate a Class object in memory as an access point to the Class data in the method area.
How to load a. Class file
- Load directly from the local system
- Download the. Class file from the network
- Load. Class files from zip, JAR, etc archives
- Extract. Class files from a proprietary database
- Dynamically compile Java source files into.class files
Connection phase
1. Verify
- The first step is to verify that the byte stream conforms to Class file specifications, such as whether the byte stream starts with module 0xCAFEBABE, whether the primary and secondary version numbers are within the scope of the current virtual machine, and so on.
- Metadata verification: Semantic analysis of the information described by bytecode to ensure that the information described conforms to the requirements of Java language specifications; For example, whether the class has a parent class, whether it implements an abstract method of the parent class, whether it overrides the final method of the parent class, whether it inherits a class modified by final, and so on.
- Bytecode verification: Through data flow and control flow analysis, to determine that the program semantics are legitimate and logical, such as: operand stack data type and instruction code sequence work together, ensure that the type conversion in the method is effective, etc.
- Symbol reference validation: Ensures that the parse action is performed correctly; For example, the corresponding class and method can be found by matching reference, and the accessibility of class, attribute and method in symbolic reference can be accessed by the current class, etc.
2. Prepare
Allocates memory for class variables and sets their initial values, all of which will be allocated in the method area. There are several points to note about this phase:
- Only static variables that are static are allocated and assigned default values (such as 0, 0L, NULL, false, etc.).
- Final static literal constants are assigned initial values directly (initial values are not assigned default values; if they are not literal static constants, they are assigned default values just like static variables).
3. The parsing
A procedural symbolic reference that replaces a symbolic reference in a constant pool with a direct reference (memory address) is a set of symbols describing the target, which can be any literal. Concepts that are part of compilation principles include fully qualified names including classes and interfaces, field names and descriptors, and method names and descriptors.
A direct reference is a pointer to a target directly, a relative offset, or a handle to the target indirectly. Such as a pointer to a class in the method area.
Assume that a class has a static variable of a custom type. After parsing, the static variable will be a pointer to the class’s memory address in the method area.
4. The initialization
Initialization is the final step in class loading and the actual execution of the Java program code (bytecode) defined in the class. The initialization phase is the execution of the class constructor < Clinit >() method.