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 showsJavaClass 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,classFile)
  • Converts the static storage structure represented by the obtained binary byte stream into the runtime data structure of the method area
  • inThe heapGenerate ajava.lang.ClassObject that serves as an access point for the class

Note that the binary byte stream can be obtained from other sources than class files:

  • fromzipPackage 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 thenum1Initialized to1
  • The contents of the static block are then executed
    • willnum1Initialized to2.
    • Then the outputstatic : 2
    • thennum2Initialized to20
  • thennum2Initialized to10
  • The last executionmainmethods

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.