N-2 days of learning about the JVM, learning about class loading and initializing active and passive references.

1. Class loading mechanism

The Java VIRTUAL machine loads the data describing the Class from the Class file to the memory, verifies, transforms, and initializes the data, and finally forms Java types that can be directly used by the VIRTUAL machine. This process is called the virtual machine Class loading mechanism.

The above paragraph is in “Understanding the Java Virtual Machine” by Zhou Zhiming, as the concept of class loading mechanism is excerpted here.

When we choose to run the following Hello World program in the compiler, we go through a series of complex steps from click to run until the program stops running. These steps are the life cycle of the class.

public class HelloWorld {
    public static void main(String[] args){
        System.out.println("Hello World"); }}Copy the code

2. Class lifecycle

The life cycle of a class is divided into seven stages: load, validate, prepare, parse, initialize, use, and unload. The three stages of verification, preparation and analysis are collectively referred to as connection.

Here is a brief introduction to each stage.

2.1 loading

During the load phase, the virtual machine finds and loads the class’s binary byte stream into the method area using the class’s fully qualified name.

We can turn on the loading order of printing classes by adding the JVM parameter -xx :+TraceClassLoading at startup.

2.2 validation

During the verification phase, the VM must ensure that the loaded classes are correct, comply with VM specifications, and do not compromise VM security.

In this phase, there are four more stages of validation: file format validation, metadata validation, bytecode validation, and symbol reference validation. For details, see Section 7.3.2 of Understanding the Java Virtual Machine (3rd Edition).

2.3 to prepare

In the preparation phase, the virtual machine allocates memory for static variables of the class and initializes them to default values.

If the base type or string of the ConstantValue attribute (that is, modified by final) is present in the field property table of a class field, the variable value is initialized to the initial value rather than the default value during the preparation phase.

Assume that the HelloWorld class above has two class variables defined as follows, and in the preparation phase, the values of these two variables are a=0 and b=2. java public class HelloWorld { private static int a = 1; private static final int b = 2; }

2.4 analytical

During the parsing phase, the virtual machine replaces symbolic references in the constant pool with direct references. If the symbolic reference points to a class that has not been loaded, the parsing phase triggers the loading of that class.

2.5 the initialization

During the initialization phase, the virtual machine assigns the correct initial values to the static variables of the class. These assignments, along with the code in the static code block, are consolidated by the compiler into a

method that is executed only once. So we can tell if a class is initialized based on whether its static code block executes.

3. Unsolicited quotes

The Java Virtual Machine specification specifies a variety of situations that trigger initialization, known as active references to classes.

  1. Classes marked as startup classes when the virtual machine starts (classes containing the main() method)
  2. Encountered in class bytecodeNew, getstatic, putstatic, invokestaticThese four instructions trigger initialization of the class.
    • These four bytecode instructions correspond to creating an instance of a class, accessing a static field of a class, setting a static field of a class, and calling a static method of a class.
    • Static fields here do not include static constants that can be determined at compile time, because static constants are stored in the caller’s constant pool, are not directly referenced to the class that defines the constant, and therefore do not trigger the initialization of the class that defines the constant.
  3. When initializing a class, if the parent class has not already been initialized, the initialization of its parent class must be triggered first.
  4. When a reflection call is made to a class using the reflection API, the class is initialized.
  5. The first time you call a MethodHandle instance, you initialize the class to which the method points.
  6. When the default method is defined in an interface, the interface should be initialized before any implementation class of the interface is initialized.

4. Passive quoting

There are some exceptions to the above active reference case that triggers initialization:

4.1 Static Fields

For static fields, only the class that directly defines the field is initialized, so referring to a static field defined in a parent class by its subclass triggers initialization of the parent class but not the subclass.

4.2 an array

Referring to a class through an array definition does not trigger initialization of the class. Because arrays are generated directly by the Java virtual machine, the following example illustrates the situation.

Define a main method in the HelloWorld class, and define two array variables in the main method as follows:

public class HelloWorld {
    public static void main(String[] args){
        int[] a = new int[10];
        MyObject[] b = new MyObject[10];
    }
}

class MyObject {
    static {
        System.out.println("MyObject class initialization..."); }}Copy the code

Then decompile the bytecode file, enter javap -c HelloWorld on the command line, and get the decompiled bytecode instruction as follows:

➜ classes git:(master) qualify javap -c HelloWorld Compiled from those who qualify"HelloWorld.java"
public class HelloWorld {
public HelloWorld();
    Code:
        0: aload_0
        1: invokespecial #1 // Method java/lang/Object."
      
       ":()V
      
        4: return

public static void main(java.lang.String[]);
    Code:
        0: bipush        10
        2: newarray       int
        4: astore_1
        5: bipush        10
        7: anewarray     #2 // class MyObject
    10: astore_2
    11: return
}
Copy the code

As you can see, array variables of primitive type are created by the instruction newarray in the bytecode. For array variables of reference type, they are created in bytecode using the anewarray directive.

Anewarray does not meet the initializing conditions described above. This is consistent with the above test code that does not execute the static code block of MyObject, that is, does not trigger the initialization of MyObject.

4.3 Constant field compilation period is uncertain

When the value of a constant field is uncertain at compile time (such as uuID.random ().tostring ()), it is not put into the constant pool of the calling class. So even if the static field is a constant (modified by the final keyword), because it is undefined at compile time, the program will use the class of the constant actively at runtime, triggering initialization of the class of the constant.

4.4 Parent Interface

When an interface is initialized, it is not required that all of its parent interface be initialized, but only when the parent interface is actually used (such as referencing constants in the interface that are determined at runtime).

Because an interface cannot define a static code block, initialization cannot be determined by the execution of a class-static code block, as a class can. But during the initialization of the interface, the compiler also generates the < Clinit >() constructor for the interface, which initializes the member variables defined in the interface.

During initialization, the virtual machine assigns the correct initial values to the static variables of the class, and the interface class also follows this rule. So we can observe whether the interface class is initialized by assigning values to static fields during the initialization phase. Here is the verification process.

Int parentA = 1/0; ParentRand constants that cannot be determined at compile time will not be put into the constant pool of the calling class InterfaceDemo. So will trigger the interface or parent interface which at least one interface class initialization (assuming we don’t know), if trigger a parent interface initialization, then 1/0 of the value will be assigned to parentA, when the virtual machine to calculate 1/0, throws Java. Lang. ArithmeticException: / by zero; If only the initialization of the subinterface is triggered, no exception is thrown.

public class InterfaceDemo {

    public static void main(String[] args) {
        System.out.println(ChildInterface.parentRand);
    }
}

interface ParentInterface {
    int parentA = 1/0;
    String parentRand = UUID.randomUUID().toString();
}

interface ChildInterface extends ParentInterface {
    String childRand = UUID.randomUUID().toString();
}   
Copy the code

Java :15 line 15 is the location defined by parentA, so it can be concluded that initialization of the parent interface is triggered when the child interface uses the parent interface.

Exception in thread "main" java.lang.ExceptionInInitializerError
    at InterfaceDemo.main(InterfaceDemo.java:10)
Caused by: java.lang.ArithmeticException: / by zero
    at ParentInterface.<clinit>(InterfaceDemo.java:15)
    ... 1 more
Copy the code

ChildRand (childInterface.childrand, childinterface.childrand, childinterface.childrand, childInterface.childrand, childInterface.childrand, ChildInterface. To sum up, this validates the fourth conclusion: when an interface is initialized, it does not require its parent interface to be fully initialized, but only when the parent interface is actually used (such as references to run-time constants in the interface).

conclusion

  1. The Java VIRTUAL machine loads the data describing the Class from the Class file to the memory, verifies, transforms, and initializes the data, and finally forms Java types that can be directly used by the VIRTUAL machine. This process is called the virtual machine Class loading mechanism.
  2. The class loading process is divided into loading, verification, preparation, parsing and initialization, among which the three stages of verification, preparation and parsing are collectively referred to as connection. And what the virtual machine does at each stage.
  3. Six cases of unsolicited citation.
  4. Examples of passive references in four types of active references.