Timing and process of class loading

From the time a class is loaded into the virtual machine memory, until it is unloaded from the memory, its entire life cycle includes: Loading , Verification , Preparation , Resolution , Initialization, and use (Using) and unloading (Unloading) 7 stages. Among them, the three parts of verification, preparation and analysis are collectively called Linking .
insert image description here
The order of the five phases of loading, verification, preparation, initialization, and unloading is determined, but not necessarily for the "parsing" phase. It can start after initialization in some cases. This is done to support Java's Runtime binding feature (also known as dynamic binding or late binding).


1. Loading

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

  1. Get the binary byte stream defining this class by its fully qualified name.
  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 the memory, as the access entry of various data of this class in the method area.

2. Verification (just understand)

It is the first step of the connection phase. The purpose of this phase 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 safety of the virtual machine itself.

But on the whole, the verification phase will roughly complete the following four stages of verification actions: file format verification , metadata verification , bytecode verification , and symbol reference verification .

2.1, file format verification

The first stage is to 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. This phase may include the following verification points:

  • Whether to start with the magic number 0xCAFEBABE.
  • Whether the major and minor version numbers are within the acceptable range of the current Java virtual machine.
  • Whether there is an unsupported constant type in the constant in the constant pool (check the constant tag).
  • Whether any of the various index values ​​pointing to constants point to non-existent constants or constants that do not conform to the type.

2.2. Metadata verification

The second stage is to perform semantic analysis on the information described by the bytecode to ensure that the information it describes meets the requirements of the "Java Language Specification". The verification points that may be included in this stage are as follows:

  • Whether this class has a parent (all classes except java.lang.Object should have a parent).
  • Whether the parent class of this class inherits a class that is not allowed to be inherited (a class modified by final).
  • If this class is not an abstract class, whether it has implemented all the methods required to be implemented in its parent class or interface.
  • Whether the fields and methods in the class conflict with the parent class (for example, the final field of the parent class is covered, or there are method overloads that do not conform to the rules, for example, the method parameters are all the same, but the return value type is different, etc.).

2.3. Bytecode Verification

The third stage of bytecode verification is the most complicated stage in the entire verification process. The main purpose is to determine whether the program semantics are legal and logical through data flow analysis and control flow analysis. After verifying the data type in the metadata information in the second stage, it is necessary to verify and analyze the method body of the class (Code attribute in the Class file) in this stage to ensure that the method of the verified class does not run Behaviors that endanger the security of virtual machines, such as:

  • Ensure that the data type of the operand stack and the instruction code sequence can work together at any time, for example, there will be no such thing as "a data of type int is placed on the operation stack, but it is loaded into the local variable table as long when it is used" such a situation.
  • It is guaranteed that any jump instructions will not jump to bytecode instructions outside the method body.

2.4. Symbol reference verification

The final stage of verification occurs when the virtual machine converts symbolic references into direct references. This conversion will occur in the third stage of the connection, the resolution stage. Symbol reference verification can be seen as a matching check of various information other than the class itself (various symbol references in the constant pool). Generally speaking, whether the class lacks or is prohibited from accessing some external resources it depends on Resources such as classes, methods, fields, etc. This stage usually needs to verify the following:

  • Whether the fully qualified name described by the string in the symbol reference can find the corresponding class.
  • Whether the method and field described by the matching method's field descriptor and simple name exist in the specified class.
  • Accessibility (private, protected, public) of classes, fields, methods in symbolic references.
  • Whether it can be accessed by the current class.

3. Prepare

The preparation stage is the stage of formally allocating memory for class variables and setting the initial value of class variables. The memory used by these variables will be allocated in the method area.


At this stage, there are two confusing concepts that need to be emphasized. First, memory allocation at this time only includes class variables (variables modified by static), not instance variables. Instance variables will be instantiated when the object is instantiated. Along with the object allocated in the Java heap.


Secondly, the initial value mentioned here is "usually" the zero value of the data type, assuming that a class variable is defined as:
public static int value = 123;

Then the initial value of the variable value after the preparation phase is 0 instead of 123, because no Java method has been executed at this time, and the putstatic instruction that assigns the value to 123 is stored in the class constructor <clinit>( ) method, so the action of assigning the value to 123 will only be executed during the initialization phase.

type of data zero value type of data zero value
int 0 boolean false
long 0L float 0.0f
short (short) 0 double 0.0d
char ‘\u0000’ reference null
byte (byte) 0

But there are also some special cases, such as the ConstantValue attribute exists in the field attribute table of the class field, then the variable value will be initialized to the value specified by the ConstantValue attribute during the preparation phase. Suppose the above example is as follows:
public static final int value = 123;

When compiling, javac will generate the ConstantValue attribute for the value, and in the preparation stage, the virtual machine will assign the value to 123 according to the ConstantValue setting


4. Analysis

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

Symbolic References (Symbolic References)
Symbolic references use a set of symbols to describe the referenced target. The symbol can be any form of literal value, as long as it can be used to locate the target without ambiguity.

Direct References A direct reference
can be a pointer directly to the target, a relative offset, or a handle that can be indirectly located to the target.


Analysis can be roughly divided into:

  • Class or interface resolution
  • field parsing
  • class method analysis
  • Interface method analysis

The following common exceptions are related to this stage:

  • java.lang.NoSuchFieldErrorAccording to the inheritance relationship from bottom to top, an error is reported when the relevant field cannot be found. (field parsing exception)
  • java.lang.IllegalAccessErrorAn error occurs when the field or method does not have access rights. (parsing exception for class or interface)
  • java.lang.NoSuchMethodErrorError when a related method cannot be found. (Exceptions occurred during class method parsing and interface method parsing)

5. Initialization

The initialization is mainly to operate the static statement in a class (the corresponding bytecode is the clinit method). The <clinit>() method is not necessary for a class or interface. If there is no static statement block in a class and no assignment operation to variables, then the compiler does not need to generate the <clinit>() method for this class.


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>( ) method, other threads need to block and wait until the active thread finishes executing the <clinit>() method. If there is a time-consuming operation in the <clinit>() method of a class, it may cause multiple processes to block.



For the initialization phase, the virtual machine specification strictly stipulateshave and only5 cases where the class must be "initialized" immediately (while loading, validating, preparing naturally need to start before that, the parsing phase does not necessarily):

  • When encountering new, getstatic, putstaticor invokestaticthese 4 bytecode instructions, if the class has not been initialized, its initialization needs to be triggered first. The most common Java code scenarios that generate these 4 instructions are:

    1. When instantiating an object using the new keyword
    2. When reading or setting a static field of a class (except static fields that are final modified and have put the result into the constant pool at compile time)
    3. When calling a static method of a class
  • When using the methods of the java.lang.reflect package to make reflective calls to a class, if the class has not been initialized, its initialization needs to be triggered first.

  • When initializing a class, if you find that its parent class has not been initialized, you need to trigger the initialization of its parent class first.

  • When the virtual machine starts, the user needs to specify a main class to be executed (the class containing the main() method), and the virtual machine first initializes the main class.

  • When using the dynamic language support of JDK 1.7, if the final analysis result of a java.lang.invoke.MethodHandle instance is the method handle of REF_getStatic, REF_putStatic, REF_invokeStatic, and the class corresponding to this method handle has not been initialized, it needs to be triggered first. its initialization.

  • When a default method newly added by JDK1.8 (interface method modified by the default keyword) is defined in an interface, if the implementation class of the interface is initialized, the interface must be initialized before it.


5.1. Common cases

  • Referencing the static fields of the parent class through the subclass will only trigger the initialization of the parent class, and will not cause the initialization of the subclass (the subclass will only be loaded)
    public class SuperClass {
          
          
    	static {
          
          
        	System.out.println("SuperClass init...");
    	}
    
    	public static int value = 123;
    }
    
    public class SubClass extends SuperClass {
          
          
    	static {
          
          
        	System.out.println("SubClass init...");
    	}
    }
    
    public class App {
          
          
    	public static void main(String[] args) {
          
          
        	System.out.println(SubClass.value);
    	}
    }
    
    insert image description here
    Here we find that the console only outputs "SuperClass init...", but not "SubClass init...". For static fields, only the class that directly defines this field will be initialized, so the parent class is referenced through its subclass The static fields defined in will only trigger the initialization of the parent class and not the initialization of the subclass.

  • Introducing a class through an array definition does not trigger initialization of this class
    public class ArrayClass {
          
          
    	static {
          
          
        	System.out.println("ArrayClass init...");
    	}
    }
    
    public class App {
          
          
    	public static void main(String[] args) {
          
          
        	SuperClass[] classArr = new SuperClass[10];
    	}
    }
    
    insert image description here
    After running, it was found that "ArrayClass init..." was not output, indicating that the initialization phase of the ArrayClass class was not triggered.

  • The constants will be stored in the constant pool of the calling class during the compilation phase. In essence, there is no direct reference to the class that defines the constant, so the initialization of the class that defines the constant will not be triggered.
    public class ConstClass {
          
          
    	static {
          
          
        	System.out.println("ConstClass init...");
    	}
    
    	public static final String HELLO_WORLD = "Hello World";
    }
    
    public class App {
          
          
    	public static void main(String[] args) {
          
          
        	System.out.println(ConstClass.HELLO_WORLD);
    	}
    }
    
    insert image description here
    After the code is executed, "ConstClass init..." is not output. This is because although the constant HELLO_WORLD in the ConstClass class is referenced in the Java source code, the value of this constant "Hello World " is stored in the constant pool of the Client class, and the references of the Client to the constant ConstClass.HELLO_WORLD are actually converted into references of the Client class to its own constant pool.

Guess you like

Origin blog.csdn.net/rockvine/article/details/124821837