preface
The Java code we write, compiled by Javac into.class files, is called bytecode. The bytecode is loaded by the JVM and parsed by the runtime interpreter into machine code for execution. The just-in-time compiler compiles the bytecode of the hot code to machine code to achieve higher execution efficiency.
The process by which the JVM loads class bytecode is called class loading. The end product of Class loading is the Class object in the heap. The Class object encapsulates the data structure of the Class in the method area and provides the programmer with an interface to access the data structure in the method area.
The following figure showsJava
Class lifecycle.
Class Loading includes Loading, Linking, and Initialization. The link phase includes validation, preparation, and parsing.
Class loading process
The first stage is the loading stage
During the loading phase, there are three things to do:
- throughFull name of the classFor classBinary byte stream(including,
class
File) - Converts the static storage structure represented by the obtained binary byte stream into the runtime data structure of the method area
- inThe heapGenerate a
java.lang.Class
Object that serves as an access point for the class
Note that the binary byte stream can be obtained from other sources than class files:
- from
zip
Package read, includingJAR,WAR
等 - Network access
- Dynamic generation at runtime, such as dynamic proxy technology
- Other files are generated, including encrypted files
The second stage is the link stage
The link phase consists of three phases: validation, preparation, and parsing.
validation
The purpose of the verification is to ensure that the information in the byte stream of the Class file meets the requirements of the VIRTUAL machine and that the loaded Class is correct.
This process involves four types of validation:
- File format validation
- Metadata validation
- Bytecode verification
- Symbolic reference validation
However, only metadata and bytecode validation really happens in the validation phase. File format validation takes place during the load phase, while symbolic reference validation takes place during the parse phase.
To prepare
The prepare phase allocates memory space for the static variable and sets the default value.
Because final variables are allocated memory at compile time, if they are static final, they are assigned directly at the prepare stage.
parsing
The parse phase replaces the symbolic reference with a direct reference. Parsing can occur before initialization (static parsing) or after initialization (dynamic parsing, such as polymorphism, late binding).
The third stage is initialized
The initialization phase is the execution of the class’s constructor method (not the constructor) clinit(). This method does not need to be defined and is a result of the javac compiler automatically collecting assignment actions for all class variables in a class and combining statements in a static code block.
The clinit() instructions are executed in order of the statement of the code, and the virtual machine must ensure that the clinit() method of a class locks synchronously across multiple threads.
A case in point
public class ClinitTest {
private static int num1 = 1;
static {
num1 = 2;
System.out.println("static : " + num1); // ok
num2 = 20;
// System.out.println("static : " + num2); // error:illegal forward reference
}
private static int num2 = 10;
public static void main(String[] args) {
System.out.println("num1 = " + num1); / / 2
System.out.println("num2 = " + num2); / / 10}}Copy the code
The output of the above code should be:
static : 2
num1 = 2
num2 = 10
Copy the code
In the prepare phase, num1 and NUM2 both allocate memory and assign a value of 0.
In the initialization phase, follow the code order:
- First the
num1
Initialized to1
- The contents of the static block are then executed
- will
num1
Initialized to2
. - Then the output
static : 2
- then
num2
Initialized to20
- will
- then
num2
Initialized to10
- The last execution
main
methods
Since NUM2 memory is allocated during the prep phase, it is possible to assign a value to it even if it is defined after a static block, but be aware that num2 cannot be called inside a static block.
Late binding
During the parsing phase, we mentioned that if late binding is implemented, the parsing will occur after initialization. So what is late binding?
Late binding (also known as dynamic binding) means that the actual type to be invoked is determined at run time, which is the basis for implementing polymorphism.
In contrast, early binding (static binding) means that the true type is determined by the compiler.
We know that superclass methods must be overridden for polymorphism to occur, so calling superclass methods and methods that cannot be overridden (static,final,private, and constructors) are pre-binding, and all other cases are late-binding.
Dynamic binding is for methods only; properties are statically bound.
/ / parent class
class Pet{
String name = "pet";
public String info(a){
return "pet"; }}/ / subclass
class Cat extends Pet{
String name = "cat";
@Override
public String info(a) {
return super.info(); }}public class BondTest {
public static void main(String[] args) {
Pet pet = newPet(); System.out.println(pet.name); System.out.println(pet.info()); }}Copy the code
The output of the above code is:
pet
cat
Copy the code
This is because the properties are statically bound and the override methods are dynamically bound.