Detailed explanation of virtual machine class loading mechanism

Posted by adguru on Mon, 17 Jan 2022 13:10:25 +0100

Detailed explanation of virtual machine class loading mechanism

1 Overview

The Java virtual machine loads the data describing the Class from the Class file into memory, verifies, converts, parses and initializes the data, and finally forms a Java type that can be directly used by the virtual machine. This process is called the Class loading mechanism of the virtual machine. In the Java language, the loading, connection and initialization of types are completed during the running of the program.

Loading timing of class 2

A type starts from being loaded into the virtual machine memory to unloading the memory. Its whole life cycle will go through seven stages: loading, verification, preparation, parsing, initialization, use and unloading. The three parts of verification, preparation and parsing are collectively referred to as connection. The sequence of occurrence is as follows:

The Java virtual machine specification strictly stipulates that there are and only six cases in which classes must be "initialized" immediately (loading, verification and preparation naturally need to start before this):

  1. When new, getstatic, putstatic or invokestatic are encountered, if the type is not initialized, its initialization phase needs to be triggered first. Typical Java code scenarios that can generate these four instructions are:
    • When instantiating an object with the new keyword
    • When reading or setting a static field of type
    • When calling a static method of a type
  2. Using Java When the method of lang.reflect package makes a reflection call to a type, if it has not been initialized, its initialization needs to be triggered first.
  3. When initializing a class, if it is found that its parent class has not been initialized, the initialization of its parent class needs to be triggered first.
  4. When the virtual machine starts, the user needs to specify a main class to execute. The virtual opportunity initializes this main class first.
  5. jdk1.7 new dynamic language support.
  6. When a jdk8 newly added default method is defined in an interface.

The behavior of these six scenarios is called active reference to a type. In addition, all reference types do not trigger initialization, which is called passive reference. Here are some examples of passive references

Example 1: referencing a static field of a parent class through a subclass will not cause subclass initialization

public class NotInitialization {
    public static void main(String[] args) {
        System.out.println(SubClass.value);  // Only "SuperClass init" will be output, not "subclass init"
        SuperClass[] superClasses = new SuperClass[10];  // "SuperClass init" will not be output
    }
}

class SuperClass {
    static {
        System.out.println("SuperClass init");
    }
    public static int value = 123;
}

class SubClass extends SuperClass {
    static {
        System.out.println("SubClass init!");
    }
}

Example 2: constants are stored in the constant pool of the calling class at the compilation stage. In essence, they are not directly referenced to the class defining constants, so initialization of defining constants will not be triggered

public class NotInitialization {
    public static void main(String[] args) {
        System.out.println(ConstClass.HELLOWORLD);  // "ConstClass init!" will not be output, Because constants are stored directly in the constant pool.
    }
}
class ConstClass {
    static {
        System.out.println("ConstClass init!");
    }

    public static final String HELLOWORLD = "hello world";
}

class Test {
    static {
        i = 0;
    }
    static int i = 1;
}

Loading process of class 3

3.1 loading

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

  1. Get the binary byte stream that defines a class by its fully qualified name.
  2. The static storage structure represented by this byte stream is transformed into the runtime data structure of the method area.
  3. Generate a Java. Net file representing this class in memory Lang. class object, as the access entry of various data of this class in the method area.

3.2 verification

Verification is the first step in 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 constraint requirements of the Java virtual machine specification and that these information will not endanger the virtual machine itself.

3.3 preparation

The preparation stage is the stage of formally allocating memory for the variables defined in the class (i.e. static variables, variables modified by static) and setting the initial value of class variables.

3.4 analysis

The parsing phase is the process of replacing symbolic references in the constant pool with direct references.

3.5 initialization

Initialization is the last stage of the class loading process. Until the initialization stage, the Java virtual machine really starts to execute the Java program code written in the class and hand over the ownership to the application program. The initialization phase is the process of executing the () method of the class constructor.

  • The () method is different from the class constructor. It does not need to explicitly call the parent constructor. Java virtual opportunity ensures that the () method of the parent class has been executed before the () method of the child class is executed. Therefore, the first () method to be executed in the Java virtual machine must be Object.

  • Since the () method of the parent class is executed first, it means that the static statement block defined in the parent class takes precedence over the variable assignment operation of the child class. The following code: the field value will be 2 instead of 1.

    public static void main(String[] args) {
        System.out.println(Sub.B);
    }
    
    static class Parent {
        public static int A = 1;
        static {
            A = 2;
        }
    }
    
    static class Sub extends Parent {
        public static int B = A;
    }
    

Class 4 loader

Class loader is used to load classes. There are three important classloaders built in the JVM. Except bootstrap classloader, other class loaders are implemented by Java and all inherit from Java lang.ClassLoader:

  1. Bootstrap classloader: the top-level loading class, which is responsible for loading% Java_ jar packages and classes in the home% / lib directory or all classes in the path specified by the - X: bootclasspath parameter.
  2. Extension Class Loader: mainly responsible for loading < Java_ All class libraries in the home > \ lib \ ext directory.
  3. AppClassLoader (application class loader): a loader for our users, which is responsible for loading all jar packages and classes in the classpath of the current application.

4.1 parental delegation model

As shown in the above figure: the workflow of the parent delegation model is that if a class loader receives a class loading request, it will not try to load the class itself, but delegate the request to the parent class loader to complete it. This is the case for class loaders at each level, so all loading requests should eventually be transmitted to the top-level startup class loader, Only when the parent loader reports that it cannot complete the load request, the child loader will try to complete the load by itself.

  • benefit

    Classes in Java have a hierarchical relationship with priority along with its class loader.

  • realization

    private final ClassLoader parent; 
    protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException
        {
            synchronized (getClassLoadingLock(name)) {
                // First, check whether the requested class has been loaded
                Class<?> c = findLoadedClass(name);
                if (c == null) {
                    long t0 = System.nanoTime();
                    try {
                        if (parent != null) {//The parent loader is not empty. Call the parent loader loadClass() method for processing
                            c = parent.loadClass(name, false);
                        } else {//The parent loader is empty. Use the bootstrap classloader to load
                            c = findBootstrapClassOrNull(name);
                        }
                    } catch (ClassNotFoundException e) {
                       //Throwing an exception indicates that the parent class loader cannot complete the load request
                    }
                    
                    if (c == null) {
                        long t1 = System.nanoTime();
                        //Try loading yourself
                        c = findClass(name);
    
                        // this is the defining class loader; record the stats
                        sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                        sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                        sun.misc.PerfCounter.getFindClasses().increment();
                    }
                }
                if (resolve) {
                    resolveClass(c);
                }
                return c;
            }
        }
    
    

4.2 destroy the parental delegation model

The parental delegation model has been "destroyed" on a large scale for three times. For details, please refer to in-depth understanding of Java virtual machine.

Topics: Java jvm