[JVM] Class life cycle

[JVM] Class life cycle

Article directory

  • [JVM] Class life cycle
    • 1. Life cycle overview
    • 2. Loading phase
    • 3. Connection stage
      • 3.1 Verification
      • 3.2 Preparation
      • 3.3 Analysis
    • 4. Initialization phase
      • 4.1 Ways to trigger initialization
      • 4.2 Situation when clinit does not exist
      • 4.3 Initialization of multiple classes
    • 5. Summary

1. Life cycle overview

The life cycle of a class is divided into 5/7 stages:

  1. Loading
  2. Linking
    • verify
    • Prepare
    • parse
  3. Initialization
  4. Using
  5. Unloading

2. Loading phase

  1. The first step in the loading phase is for the class loader to obtain bytecode information in the form of a binary stream through different channels based on the fully qualified name of the class.
  2. After the class loader loads the class, the Java virtual machine saves the information in the bytecode to the method area.
  3. Generate an InstanceKlass object to save all the information of the class, including information that implements specific functions such as polymorphism.

image-20231024230054185

  1. At the same time, the Java virtual machine will also generate a java.lang.Class object in the heap that is similar to the data in the method area. The function is to obtain class information and store static field data in Java code (jdk8 and later)

image-20231024230311011

For developers, they only need to access the Class object in the heap and do not need to access all the information in the method area. In this way, the Java virtual machine can well control the scope of data access by developers.

image-20231024230504548

3. Connection phase

The connection phase can be divided into three small phases:

  1. Verification: Verify whether the content meets the “Java Virtual Machine Specification”
  2. Preparation: Assign initial values to static variables
  3. Parsing: Replace symbol references in the constant pool with direct references to memory

3.1 Verification

The first step in the Linking phase is verification. The main purpose of verification is to detect whether Java bytecode files comply with the constraints in the “Java Virtual Machine Specification”.

It mainly includes 4 parts (see “Java Virtual Machine Specification” for details):

  1. File format verification: For example, detect whether the bytecode file starts with CAFEBABE and whether the major and minor version numbers meet the requirements of the current Java virtual machine version.

  2. Meta information verification: For example, a class must have a parent class (super cannot be empty).

    image-20231024232536154

  3. Verify the semantics of program execution instructions: for example, instructions within a method jump to incorrect locations during execution.

  4. Symbol reference verification: For example, whether private methods in other classes are accessed, etc.

3.2 Preparation

The preparation phase allocates memory for static variables and sets initial values. Each basic data type and reference data type has its initial value.

image-20231024232844953

final Static variables of basic data types modified by keywords will be directly assigned the value in the code during the preparation phase.

image-20231024233113290

3.3 Analysis

The parsing phase mainly replaces symbol references in the constant pool with direct references. Symbolic reference is to use numbers in bytecode files to access the contents of the constant pool.

image-20231024233742347
image-20231024233754472

4. Initialization phase

  • The initialization phase executes the code in the static code block and assigns values to static variables.
  • The initialization phase will execute the bytecode instructions in the clinit section of the bytecode file.

image-20231024235051409
image-20231024235108640
image-20231024235213775

We found that when the relative position of the static code block and the static variable is changed, the position of the bytecode instructions will also change. In other words, the execution order in the clinit method is consistent with the order written in Java.

4.1 Ways to trigger initialization

There are several ways to initialize a class:

  1. Access static variables or static methods of a class. Note that if the variable is final and the right side of the equal sign is a constant, initialization will not be triggered.

    public static final value = 1; // Initialization will not be triggered because the value has been assigned during the connection (preparation) phase.
    
  2. Call Class.forName(String className) .

  3. new an object of this class.

  4. The current class that executes the main method.

Note: Adding the -XX: + TraceClassLoading parameter can print out the loaded and initialized classes.

4.2 Situation when clinit does not exist

The clinit directive does not appear under certain circumstances:

  1. There are no static code blocks and no static variable assignment statements.
  2. There are declarations of static variables, but no assignment statements.
  3. Static variables are defined using the final keyword, and such variables will be initialized directly during the preparation phase.

4.3 Initialization of multiple classes

  • Directly accessing static variables of the parent class will not trigger the initialization of the subclass.
  • Before calling the subclass’s initialization clinit, the parent class’s clinit initialization method will be called first.

image-20231025001252181
image-20231025001305872

5. Summary

The life cycle of a class is divided into 5 stages:

  1. Loading: Load the contents of the bytecode file according to the fully qualified name of the class and convert it into appropriate data, put it into the memory, and store it in the method area and heap.
  2. Connect:
    • Verification: Magic numbers, version numbers and other verifications generally do not require programmers to pay attention.
    • Preparation: Allocate memory for static variables and set initial values.
    • Parsing: Replace the symbol reference (number) in the constant pool with a direct reference (memory address)
  3. Initialization: Execute static code blocks and assignment of static variables.
  4. Use
  5. Uninstall

Key points to note:

  1. When a static variable is modified with the final keyword, the assignment operation is completed in the preparation phase.
  2. Directly accessing static variables of the parent class will not trigger the initialization of the subclass.
  3. Before the subclass’s initialization clinit is called, the parent class’s clinit initialization method will be called first.
  4. Add the -XX: + TraceClassLoading parameter to print out the loaded and initialized classes on the console