18, Detailed description of class loading process

Posted by seatoomol7 on Mon, 03 Jan 2022 22:25:26 +0100

1, Foreword

1. This article is right Class loading subsystem Supplement to.
2. In Java, data types are divided into basic data types and reference data types. The basic data type is pre-defined by the virtual machine, and the reference data type needs to be loaded.

2, Loading phase

1. Understanding of loading

1. The Loading stage is a stage in the whole Class Loading process.
2. Loading is to load the bytecode file of Java class into machine memory, and build the prototype of Java class - class template object in memory.
3. The class template object is actually a snapshot of the Java class in the JVM memory. The JVM stores the constant pool, class fields, class methods and other information parsed from the bytecode file into the template, so that the JVM can obtain any information in the Java class through the class template at runtime, traverse the member variables of the Java class, and call Java methods.
4. The reflection mechanism is implemented based on this stage. If the JVM does not store the declaration information of Java classes, the JVM cannot reflect at run time.
5. What is done in the loading phase:
  • Get the binary byte stream defining a class through the fully qualified name of the class.
  • The static storage structure represented by this byte stream is transformed into the runtime data structure (Java class model) of the method area.
  • Generate a Java. Net file representing this class in memory Lang. class object, as the entry of various data access of this class in the method area.

2. Binary stream acquisition method

1. For the binary data stream of class, virtual machine can generate or obtain it in many ways. (as long as the read bytecode conforms to the JVM specification)
  • The virtual machine may read a file with a class suffix through the file system (the most common)
  • Read jar, zip and other archive packages and extract class files
  • Binary data of classes stored in the database in advance
  • Load over the network using a protocol like HTTP
  • Generate binary information of a Class at runtime
2. After getting the binary information of the class, the Java virtual machine will process the data and finally turn it into a Java Instance of lang.class
3. If the input data is not a ClassFile structure, a ClassFormatError will be thrown

3. Location of Class model and Class instance

1. Location of class model: the loaded class creates the corresponding class structure in the JVM, and the class structure will be stored in the method area (in the permanent generation before JDK 1.8; in the meta space after JDK 1.8).
2. Location of class instance: class will After the class file is loaded into the meta space, a Java. Class file will be created in the heap Lang. class object is used to encapsulate the data structure of the class in the method area. The class object is created during class loading, and each class corresponds to an object of class type. Figure: the external can access the class data structure of Order by accessing the class object representing the Order class


3. Note: the construction method of Class class is private, and only JVM can create it.
4,java.lang.Class instance is not only an interface to access type metadata, but also a key data and entry to realize reflection. Through the interface provided by class, you can obtain the information associated with the target class The specific data structure (method, field and other information) in the class file.

4. Loading of array classes

1. The case of creating an array class is slightly special, because the array class itself is not created by the class loader, but directly created by the JVM as needed at runtime, but the element type of the array still needs to be created by the class loader. Procedure for creating an array class:
  • If the element type of the array is a reference type, the element type of the array is loaded and created recursively following the defined loading process.
  • The JVM creates a new array class using the specified element type and array dimension.
2. If the element type of the array is a reference type, the accessibility of the array class is determined by the accessibility of the element type. Otherwise, the accessibility of the array class will be defined as public by default.

3, Linking phase

1. Verification phase

1. When the class is loaded into the system, the link operation starts. Verification is the first step of the link operation. Its purpose is to ensure that the loaded bytecode is legal, reasonable and in line with the specification. The verification steps are complex, and there are many items to be verified. Generally, the Java virtual machine needs to do the following checks, as shown in the figure:


2. Overall description: the content of verification covers format verification, semantic check, bytecode verification and symbol reference verification of class data information
  • Format validation is performed together with the loading phase. After the verification, the class loader will successfully load the binary data information of the class into the method area.
  • Validation operations other than format validation will be performed in the method area.
3. Although the verification in the link phase slows down the loading speed, it avoids various checks when the bytecode is running.

2. Specific description of verification

1. Format check: whether it starts with magic number 0xCAFEBABE, whether the major version and minor version numbers are within the support range of the current Java virtual machine, whether each item in the data has the correct length, etc.
2. Semantic check: the Java virtual machine checks the semantics of bytecode. If the semantics does not meet the specification, the virtual machine will not pass the verification
  • Whether all classes have a parent class (in Java, all classes except Object should have a parent class).
  • Whether some methods or classes defined as final have been overridden or inherited.
  • Whether the non abstract class implements all abstract methods or interface methods.
  • Is there an incompatible method (for example, the signature of the method is the same except for the return value, which will make the virtual machine unable to start scheduling; the method in the case of abstract cannot be final)
3. Bytecode verification: it is also the most complex process in the verification process. It tries to judge whether bytecode can be executed correctly by analyzing bytecode stream
  • Whether to jump to a non-existent instruction during the execution of bytecode.
  • Whether the call to the function passed the parameters of the correct type.
  • Whether the assignment of variables is given the correct data type, etc.
  • Add: stack maptable is used at this stage to detect whether the local variable table and operand stack have the correct data type at a specific bytecode. Unfortunately, it is impossible to judge whether a bytecode can be executed safely with 100% accuracy. Therefore, the process only checks out predictable and obvious problems as far as possible. If the check fails at this stage, the virtual machine will not load the class correctly. However, if you pass the inspection at this stage, it does not mean that this class is completely free of problems.
4. Symbol reference verification: the Class file will record other classes or methods it will use through strings in its constant pool. Therefore, in the verification phase, the virtual machine will check that these classes or methods do exist, and the current Class has permission to access these data. If a Class that needs to be used cannot be found in the system, NoClassDefFoundError will be thrown. If a method cannot be found, NoSuchMethdError will be thrown, and this phase will be executed in the parsing phase.

3. Preparation

1. The Preparation phase allocates memory for the static variables of the class and initializes them to their default values.
2. When a class passes validation, the virtual machine enters the preparation phase. At this stage, the virtual machine will allocate the corresponding memory space for this class and set the default initial value. The default initial values of Java virtual machine for various types of variables are as follows:


3. Note:
  • Java does not support boolean types. For boolean types, the internal implementation is int. since the default value of int is 0, the corresponding default value of boolean is false.
  • There is no case where the fields of basic data types are decorated with static final, because final will be allocated during compilation and will be explicitly assigned during preparation.
  • Note that initialization will not be allocated for instance variables here, class variables will be allocated in the method area, and instance variables will be allocated to the Java heap along with the object.
  • In this phase, there will be no initialization or code execution as in the initialization phase.

4. Resolution

1. After the preparation phase is completed, it enters the parsing phase. The Resolution phase converts symbolic references of classes, interfaces, fields and methods into direct references.
2. Symbolic reference: refers to some literal references, which have nothing to do with the internal data structure and memory distribution of the virtual machine. It is in the Class file that a large number of symbol references are made through the constant pool. However, when the program is actually running, only symbolic reference is not enough. For example, when the following println() method is called, the system needs to know the location of the method.
  • The Java virtual machine prepares a method table for each class and lists all its methods in the table. When you need to call a class's method, you can call the method directly as long as you know the offset of the method in the method table. Through the parsing operation, the symbolic reference can be transformed into the position of the target method in the method table in the class, so that the method can be called successfully.


3. Summary:
  • Parsing is to convert a symbolic reference to a direct reference, that is, to get the pointer or offset of a class, field, or method in memory. Therefore, it can be said that if the direct reference exists, it is certain that the class, method or field exists in the system. However, there are only symbolic references, and it is not certain that the structure must exist in the system.
  • However, the Java virtual machine specification does not explicitly require that the parsing phase must be executed in sequence. In the HotSpot VM, loading, validation, preparation, and initialization are performed in an orderly manner, but the parsing operations in the link phase are often followed by the JVM after initialization.

4, Initialization phase

1. Overview

1. The initialization phase assigns the correct initial value (explicit assignment) to the static variable of the class.
2. Class initialization is the last stage of class loading. If the previous steps are OK, the presentation class can be loaded into the system smoothly. At this point, the class will start executing Java bytecode. (that is, the Java program code defined in the class is not really executed until the initialization stage)
3. The important work in the initialization phase is to execute the initialization method of the class: < clinit > () method
  • This method can only be generated by the Java compiler and invoked by JVM. The programmer can not customize a method with the same name, and can not call the method directly in the Java program. The method is also composed of bytecode instructions.
  • It is generated by the combination of assignment statements of class static members and static statement blocks.


4. Before loading a class, the virtual machine will always try to load the parent class of the class, so the < clinit > of the parent class is always called before the < clinit > of the child class, that is, the static block of the parent class takes precedence over the child class. By parent and child, static first (static first execution).
5. The Java compiler does not generate < clinit > () initialization methods for all classes. After the following classes are compiled into bytecode, the bytecode file will not contain the < clinit > () method.
  • When a class does not declare any class variables or static code blocks.
  • When a class variable is declared in a class, but the initialization statement and static code block of the class variable are not explicitly used to perform the initialization operation.
  • A class contains fields of basic data type modified by static final. The initialization statements of these class fields adopt compile time constant expressions.
/**
 * @Date: 2021/12/22
 * In those scenarios, the java compiler will not generate < clinit > () methods
 * Check the methods in bytecode through jclasslib and find that there is no < clinit > () method
 */
public class InitializationTest1 {
    //Scenario 1: for non static fields, the < clinit > () method will not be generated regardless of whether the display assignment is performed
    public int num;

    public int num1 = 1;

    //Scenario 2: static fields without explicit assignment will not generate < clinit > () methods
    public static int num2;

    //Scenario 3: the < clinit > () method will not be generated for fields of basic data type declared as static final, regardless of whether the display assignment is performed or not
    public static final int num3 = 1;
}

2. Collocation of static and final

/**
 * @Date: 2021/12/30
 * At what stage is the explicit assignment of fields decorated with static + final performed?
 * Case 1: assignment in the preparation phase of the link phase
 * Case 2: assignment in initialization phase < clinit > ()
 */
public class InitializationTest2 {
    public static int a = 1;//Assignment in initialization phase < clinit > ()
    public static final int b = 10;//Assignment in the preparation phase of the link phase

    public static final Integer c = Integer.valueOf(100);//Assignment in initialization phase < clinit > ()
    public static Integer d = Integer.valueOf(1000);//Assignment in initialization phase < clinit > ()

    public static final String e = "hello1";//Assignment in the preparation phase of the link phase
    public static final String f = new String("hello2");//Assignment in initialization phase < clinit > ()

    public static String g = "hello3";//Assignment in initialization phase < clinit > ()

    public static final int h = new Random().nextInt(10);//Assignment in initialization phase < clinit > ()
}
1. View bytecode through jclasslib


2. Description of assignment in the preparation phase of the link phase:
  • For fields of basic data types, if static final is used to modify them, the display assignment (direct assignment of constants rather than calling methods) is usually carried out in the preparation phase of the link phase.
  • For String, if literal assignment is used and static final modification is used, the display assignment is usually carried out in the preparation phase of the link phase.
3. Assignment in initialization phase < clinit > ():
  • Excluding the above assignment in the preparation phase.
4. Final conclusion: the static + final modification is used, and the explicit assignment does not involve the explicit assignment of the basic data type or String type called by the method or constructor, which is carried out in the preparation phase of the link phase; Other cases are assigned in the initialization phase < clinit > ().

3. Thread safety of clinit method

1. For the call of < clinit > () method, that is, the initialization of class, the virtual opportunity internally ensures its security in the multithreaded environment
2. Virtual opportunity ensures that the < clinit > () method of a class is locked and synchronized correctly in a multithreaded environment. If multiple threads initialize a class at the same time, only one thread will execute the < clinit > () method of this class, and other threads need to block and wait until the active thread finishes executing the < clinit > () method.
3. It is precisely because the function < clinit > () is thread safe with locks. Therefore, if an operation takes a long time in the < clinit > () method of a class, it may cause multiple thread blocking and deadlock. And such deadlocks are hard to find because they seem to have no available lock information.
4. If the previous thread successfully loads the class, the thread waiting in the queue will have no chance to execute the < clinit > () method. Then, when this class needs to be used, the virtual opportunity directly returns the information it has prepared.
/**
 * @Date: 2021/12/31
 * Deadlock problem
 */
public class StaticDeadLockTest extends Thread {
    private char flag;

    public StaticDeadLockTest(char flag) {
        this.flag = flag;
        this.setName("Thread" + flag);
    }

    @Override
    public void run() {
        try {
            Class.forName("com.itan.middle.day3.Static" + flag);
        } catch (ClassNotFoundException e) {

        }
        System.out.println(getName() + "over");
    }

    public static void main(String[] args) {
        StaticDeadLockTest loadA = new StaticDeadLockTest('A');
        loadA.start();
        StaticDeadLockTest loadB = new StaticDeadLockTest('B');
        loadB.start();
    }
}

class StaticA {
    static {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {

        }
        try {
            Class.forName("com.itan.middle.day3.StaticB");
        } catch (ClassNotFoundException e) {

        }
        System.out.println("StaticA init ok");
    }
}

class StaticB {
    static {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {

        }
        try {
            Class.forName("com.itan.middle.day3.StaticA");
        } catch (ClassNotFoundException e) {

        }
        System.out.println("StaticB init ok");
    }
}

4. Active use of classes

1. Java programs use classes in two ways: active use (calling the < clinit > () method of the class and executing the initialization phase of the class) and passive use.
2. Class is loaded only when it must be used for the first time. Java virtual machine will not load class type unconditionally. Java virtual machine stipulates that a class or interface must be initialized before it is used for the first time. This refers to "use", which refers to active use. Active use can only be used in the following situations (that is, if the following situations occur, the class will be initialized, and the loading, verification and preparation before the initialization operation have been completed).
  1. When creating an instance of a class, use the new keyword, or reflect, clone, or deserialize.
  2. When a static method of a class is called, the bytecode instruction invokestatic is used.
  3. When using static fields of classes and interfaces (special consideration for final modification), for example, use bytecode instructions getstatic or putstatic (operation of accessing and assigning variables).
  4. When using Java When a method in the lang.reflect package reflects a method of a class. For example: class forname(“com.itan.java.Test1”).
  5. When initializing a child class, if it is found that its parent class has not been initialized, it is necessary to trigger the initialization of its parent class first.
  6. If an interface defines a default method, the class that directly or indirectly implements the interface should be initialized before it.
  7. 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 initializes the main class first.
  8. When the MethodHandle instance is called for the first time, the class of the method pointed to by the MethodHandle is initialized. (involves parsing the classes corresponding to the handles of REF_getStatic, REF_putStatic and REF_invokeStatic methods).
/**
 * @Date: 2021/12/31
 * Active use of the test class: the < clinit > () method of the class will be called to execute the initialization phase of the class
 * 1. When creating an instance of a class, use the new keyword, or reflect, clone, or deserialize.
 * 2. When a static method of a class is called, the bytecode instruction invokestatic is used.
 */
public class ActiveUse1 {
    public static void main(String[] args) throws InterruptedException {
        //After executing this code, the Order init will be printed out. Through the bytecode, the < clinit > () method is generated in the Order class
        Order order = new Order();
    }

    //The serialization process generates a file through serialization to facilitate deserialization testing
    @Test
    public void test1() {
        ObjectOutputStream oss = null;
        try {
            oss = new ObjectOutputStream(new FileOutputStream("order.dat"));
            oss.writeObject(new Order());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (oss != null) {
                    oss.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    //In the process of deserialization, Order init is also printed
    @Test
    public void test2() {
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new FileInputStream("order.dat"));
            Order order = (Order) ois.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            try {
                if (ois != null) {
                    ois.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Active use of test classes for the second point
     */
    @Test
    public void test3() {
        Order.method1();
    }
}

class Order implements Serializable {
    static {
        System.out.println("Order init");
    }

    public static void method1() {
        System.out.println("Order static method()");
    }
}
/**
 * @Date: 2021/12/31
 * Active use of the test class: the < clinit > () method of the class will be called to execute the initialization phase of the class
 * 3. When using static fields of classes and interfaces (special consideration for final modification), for example, use bytecode instructions getstatic or putstatic (operation of accessing and assigning variables).
 */
public class ActiveUse2 {
    @Test
    public void test1() {
        // System.out.println(User.num1);// User init is output
        // System.out.println(User.num2);// User init is not output
        System.out.println(User.num3);//User init is output
    }

    @Test
    public void test2() {
        // System.out.println(InterfaceA.num1);// interfaceA init is not output
        System.out.println(InterfaceA.num2);//interfaceA init is output
    }
}

class User {
    //The static field has explicit assignment, so the < clinit > () method of the class will be called
    public static int num1 = 1;
    //final is decorated and constant is directly assigned, so the < clinit > () method of the class will not be called
    public static final int num2 = 1;
    //final decoration, but the subsequent assignment involves the call of the method, so the < clinit > () method of the class will be called
    public static final int num3 = new Random().nextInt(10);

    static {
        System.out.println("User init");
    }
}

interface InterfaceA {
    //The instance decorated with static final will generate < clinit > () method because it is assigned by calling method. Therefore, as long as InterfaceA init is output, it means that class initialization is performed
    public static final Object t = new Object() {
        //Construct code block
        {
            System.out.println("InterfaceA init");
        }
    };
    //final modification and direct assignment of constants, so the < clinit > () method of the class will not be called;
    public static final int num1 = 1;
    //final modification, but the subsequent assignment involves method call, so the < clinit > () method of the class will be called;
    public static final int num2 = new Random().nextInt(10);
}
/**
 * @Date: 2021/12/31
 * Active use of the test class: the < clinit > () method of the class will be called to execute the initialization phase of the class
 * 4. When using Java When a method in the lang.reflect package reflects a method of a class. For example: class forname("com.itan.java.Test1"). 
 * 5. When initializing a child class, if it is found that its parent class has not been initialized, it is necessary to trigger the initialization of its parent class first.
 * Supplementary notes for 5:
 * When the Java virtual machine initializes a class, it requires that all its parent classes have been initialized, but this rule does not apply to interfaces
 * - When initializing a class, the interface it implements is not initialized first
 * - When initializing an interface, its parent interface is not initialized first
 * - Therefore, a parent interface will not be initialized because of the initialization of its child interface or implementation class. It will be initialized only when the program uses the static field of a specific interface for the first time
 */
public class ActiveUse3 {
    @Test
    public void test1() throws ClassNotFoundException {
        //Order init is output
        Class clazz = Class.forName("com.itan.middle.day4.Order");
    }

    @Test
    public void test2() {
        //Son.num will trigger the initialization of son class, but it will trigger the initialization of Father class before initialization
        System.out.println(Son.num);//Output Father init and Son init
    }

    @Test
    public void test3() {
        /**
         * Father Class implements the interface, but when initializing the Father class, InterfaceB init is not output,
         * Therefore, note: when initializing a class, the interface it implements will not be initialized first
         */
        System.out.println(Father.num);
    }

    @Test
    public void test4() {
        /**
         * InterfaceC It inherits the InterfaceB interface, but does not output InterfaceB init when initializing InterfaceC,
         * Therefore, note: when initializing an interface, its parent interface will not be initialized first
         */
        System.out.println(InterfaceC.num);
    }
}

class Father {
    static {
        System.out.println("Father init");
    }

    public static int num = 1;
}

class Son extends Father {
    static {
        System.out.println("Son init");
    }

    public static int num = 2;
}

interface InterfaceB {
    public static final Object t = new Object() {
        //Construct code block
        {
            System.out.println("InterfaceB init");
        }
    };
}

interface InterfaceC extends InterfaceB {
    public static final Object t = new Object() {
        //Construct code block
        {
            System.out.println("InterfaceC init");
        }
    };

    public static final int num = new Random().nextInt(10);
}
/**
 * @Date: 2021/12/31
 * Active use of the test class: the < clinit > () method of the class will be called to execute the initialization phase of the class
 * 6. If an interface defines a default method, the class that directly or indirectly implements the interface should be initialized before it.
 * 7. 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 initializes the main class first.
 * Supplementary notes for 7:
 * - JVM At startup, load an initial class through the boot class loader. This class is linked and initialized before calling the main method. The execution of this method will result in the loading, linking, and initialization of the required classes in turn
 */
public class ActiveUse4 {
    static {
        System.out.println("ActiveUse4 init");
    }
    @Test
    public void test1() {
        /**
         * Father1 The InterfaceB1 interface is implemented. There is a default method in the InterfaceB1 interface, so the interface will be initialized when Father1 is initialized
         * Therefore, it is proved that if an interface defines the default method, the class that directly or indirectly implements the interface should be initialized before it.
         */
        System.out.println(Father1.num);
    }

    public static void main(String[] args) {
        /**
         * ActiveUse4 init is output
         * Therefore, it is proved that when the virtual machine starts, specify a main class to be executed (the class containing the main() method), and the virtual opportunity initializes the main class first.
         */
        System.out.println("main");
    }
}

class Father1 implements InterfaceB1 {
    static {
        System.out.println("Father init");
    }

    public static int num = 1;
}

interface InterfaceB1 {
    public final static Object t = new Object() {
        //Construct code block
        {
            System.out.println("InterfaceB1 init");
        }
    };

    public default void method() {
        System.out.println("Interface default method");
    }
}

5. Passive use of classes

1. In addition to the above situations, all other situations belong to passive use. Passive use will not cause class initialization, nor will it call the < clinit > () method, that is, classes that do not appear in the code will be loaded or initialized. If the conditions for active use are not met, the class will not be initialized.
2. Several situations:
  1. When accessing a static field, only the class that actually declares the field is initialized. That is, when a static variable of the parent class is referenced through a subclass, subclass initialization will not be caused.
  2. Defining a class reference through an array does not trigger the initialization of this class.
  3. Reference constants do not trigger initialization of this class or interface. Because constants are explicitly assigned at the link stage
  4. Calling the loadClass() method of ClassLoader class to load a class is not an active use of the class and will not lead to class initialization
/**
 * @Date: 2022/1/2
 * Passive use of the test class: that is, the < clinit > () method of the class will not be called and the initialization phase of the class will not be executed
 * 1. When accessing a static field, only the class that actually declares the field will be initialized. When the static variable of the parent class is referenced through the subclass, the subclass will not be initialized.
 * 2. Defining a class reference through an array does not trigger the initialization of this class.
 *    Parent[] parents = new Parent[10]; //Initialization of the Parent class is not triggered, but the Parent class is loaded
 *    parents[0] = new Parent();  //Array elements are initialized only when they are assigned a value
 */
public class PassiveUse1 {
    @Test
    public void test1() {
        //Referencing the static variable of the parent class through the subclass will not cause the subclass to be loaded, so Child init will not be output
        System.out.println(Child.num);
    }

    @Test
    public void test2() {
        //Defining a class reference through an array will not trigger the initialization of this class, but the Parent class will be loaded, so the Parent init will not be output
        Parent[] parents = new Parent[10];
    }

    @Test
    public void test3() {
        Parent[] parents = new Parent[10];
        //It is initialized only when the array element is assigned a value, so Parent init is output
        parents[0] = new Parent();
        //Parent init will not be output again because the < clinit > () method will only be executed once
        parents[1] = new Parent();
    }
}

class Parent {
    static {
        System.out.println("Parent init");
    }

    public static int num = 1;
}

class Child extends Parent {
    static {
        System.out.println("Child init");
    }
}
/**
 * @Date: 2022/1/2
 * Passive use of the test class: that is, the < clinit > () method of the class will not be called and the initialization phase of the class will not be executed
 * 3. Reference constants do not trigger initialization of this class or interface. Because constants are explicitly assigned in the preparation phase of the link phase
 * 4. Calling the loadClass() method of ClassLoader class to load a class is not an active use of the class and will not lead to class initialization
 */
public class PassiveUse2 {
    @Test
    public void test1() {
        //Parent1 init is not output
        System.out.println(Parent1.num);
    }

    @Test
    public void test2() {
        //Parent1 init is not output
        System.out.println(InterfaceC1.num);
    }

    @Test
    public void test3() throws ClassNotFoundException {
        /**
         * Parent1 init is not output
         * Therefore, it is proved that calling the loadClass() method of ClassLoader class to load a class is not an active use of the class and will not lead to class initialization
         */
        Class clazz = ClassLoader.getSystemClassLoader().loadClass("com.itan.middle.day4.Parent1");
    }
}

class Parent1 {
    static {
        System.out.println("Parent1 init");
    }

    public static final int num = 1;//In the preparation phase of the link phase, it is assigned to 1
}

interface InterfaceC1 {
    public final static Object t = new Object() {
        //Construct code block
        {
            System.out.println("InterfaceC1 init");
        }
    };

    public static final int num = 1;//In the preparation phase of the link phase, it is assigned to 1
}

5, Use and uninstall phase of class

1. Class usage

1. Before any type is used, it must go through three steps: complete loading, linking and initialization. Once a type has successfully gone through these three steps, it will "everything is ready, only the east wind" and wait for use.
2. We can access and call its static class member information (such as static fields and static methods) in the program, or use the new keyword to create an object instance for it.

2. Necessary description

1. Reference relationships among classes, class loaders, and class instances
  • In the internal implementation of the Class loader, a Java collection is used to store the references of the loaded classes. On the other hand, a Class object always refers to its Class loader. You can get its Class loader by calling the getClassLoader() method of the Class object. It can be seen that there is a two-way association between the Class instance representing a Class and its Class loader.
  • The instance of a Class always refers to the Class Object representing the Class. The getClass() method is defined in the Object Class, which returns the reference of the Class Object representing the Class to which the Object belongs. In addition, all Java classes have a static attribute Class, which refers to the Class Object representing this Class.
2. Class lifecycle
  • When the Sample Class is loaded, linked, and initialized, its life cycle begins. When the Class object representing the Sample Class is no longer referenced, that is, untouchable, the life cycle of the Class object will end, and the data of the Sample Class in the method area will be unloaded, thus ending the life cycle of the Sample Class.
  • When a Class ends its life cycle depends on when the Class object representing it ends its life cycle.
3. Specific examples
  • loader1 variable and obj variable indirectly refer to the Class object representing the Sample Class, while objClass variable directly refers to it.
  • If the three reference variables on the left are set to null during the program running, the life cycle of the Sample object ends, the life cycle of the MyClassLoader object ends, the life cycle of the Class object representing the Sample Class also ends, and the binary data of the Sample Class in the method area is unloaded.
  • When it is necessary again, it will check whether the Class object of the Sample Class exists. If it exists, it will be used directly and will not be reloaded; If the Sample Class does not exist, it will be reloaded, and a new Class instance representing the Sample Class will be generated in the heap of the Java virtual machine (you can check whether it is the same instance through the hash code).

3. Unloading of class

1. The type loaded by the startup class loader cannot be unloaded during the whole run (JVM and JSL specifications).
2. The types loaded by the system class loader and extension class loader are unlikely to be unloaded during operation, because the system class loader instance or extension class instance can basically be accessed directly or indirectly during the whole operation, and it is very unlikely to be unreachable.
3. The type loaded by the custom classloader instance can only be unloaded in a very simple context, and generally can be done by forcibly calling the garbage collection function of the virtual machine. It can be expected that in slightly more complex application scenarios (for example, in many cases, users use caching strategy to improve system performance when developing loader instances of custom classes), the loaded types are almost unlikely to be unloaded during operation (at least the unloading time is uncertain).
4. Based on the above three points, the probability of a loaded type being unloaded is very small, and at least the unloading time is uncertain. At the same time, we can see that when developing code, developers should not realize specific functions in the system on the premise of virtual machine type unloading.