directory

• Write it first

• Timing of class loading

• The process of class loading

loading

validation

To prepare

parsing

Initialize the

• Class loaders


• Write it first

Class loading is not a mystery, you can say that we are being classloaded all the time, but most of the time we don’t need to pay attention to how class loading affects us, but if you look at how class loading works, you can see that this is how our everyday code works. Even for all kinds of code written inheritance, local library methods, network methods, to point before did not dare to try the SAO operation. Why is class loading so interesting? What is it? This is because C/C++ is a compile-time concatenation language, whereas Java’s type loading, concatenation, and initialization are all done at runtime. This strategy adds a slight performance overhead to class loading. But to provide Java applications with a high degree of flexibility, Java’s dynamically extensible language features rely on runtime dynamic loading and load connections. To give you an example, if we serialize a type to store on the server or in a database, we can get the byte stream from the program, deserialize the byte stream and load it into the program at Java runtime, which you can’t imagine in C++. This approach to Java is already very widely used and extremely useful (I’m not saying C++ is bad, after all, Java loaders are implemented in C++, hahaha).

• Timing of class loading

The whole life cycle of a class includes seven stages: loading, validation, preparation, parsing, initialization, use, and unloading. The three parts of verification, preparation and parsing are collectively called connection, which is roughly shown in the figure below.

It is worth noting that there is a definite order in the five stages of loading, validation, preparation, initialization, and writing. The loading process of a class must begin in this order, while the parsing phase may not. Note that only the five stages are guaranteed to start in order, not to finish in order. When does a virtual machine need to start the first phase of the class loading process? To recap, the Java Virtual Machine specification strictly states that there are only five cases in which classes must be “initialized” immediately (while loading, validation, and preparation naturally need to begin before that). What are the other five cases that are not enforced?

• New, Genstatic, pustatic, or Invokestatic are bytecode instructions that need to be initialized if the class has not already been initialized (for those who don’t know bytecode, check out my other article, Java class file structure). For the scenarios where we generate these four instructions in code: When you instantiate an object with the new keyword, when you read or set a class’s static fields (except those that are modified by final and have been put into the constant pool by the compiler), and when you call a class’s static methods.

• When a reflection call is made to a class using the java.lang.Reflect package’s methods, initialization needs to be triggered if the class has not already been initialized.

• When initializing a class, if the parent class has not been initialized, the initialization of the parent class must be triggered first.

• When a VM starts, the user needs to specify a primary class to execute. The VM initializes this primary class first.

When using a dynamic language support, if a Java lang. Invoke. The final analytical results REF_getStatic MethodHandle instance, REF_putStatic, REF_invokeStatic method handles, If the class to which the method handle corresponds has not been initialized, it needs to be initialized first.

• The process of class loading

Next, we will take a detailed look at the whole process of class loading in Java virtual machine, that is, loading, verification, preparation, parsing, initialization of the five stages.

loading

First of all, don’t confuse “loading” with “class loading.” Loading is just one phase of class loading, and during the loading phase, the VIRTUAL machine specification states that a virtual machine needs to do three things:

1. Get the binary byte stream that defines a class by its fully qualified name (see my other article on class file structures);

2. Convert the current storage structure represented by this byte stream to the runtime data structure of the method area;

Generate an in-memory java.lang.Class object representing the Class as an access point to the Class’s various data in the method area.

Since the Java Virtual machine definition of this specification is not specific, like where we read the binary stream, this allows us to use a lot of convenient techniques, We can read it from ZIP packages (the JSR, EAR, WAR format we use), we can get it from the web (if you’ve heard of applets), we can generate it at run time (dynamic proxy technology does this), and we can generate the corresponding Class from JSP files, And reading from the database and so on.

It is worth mentioning that the loading phase of a non-array class is the most developer controllable, because the loading phase can be done either using the system-provided boot class loader or by a user-defined class loader (classloaders come later). Data classes are created directly by the Java VIRTUAL machine, rather than by the classloader. (Note, however, that data classes refer to things like String[], not the strings inside, which are created by the classloader.)

validation

Validation is the first step in the connection phase, which ensures that the byte stream in the Class file meets the requirements of the current virtual machine and does not compromise the security of the virtual machine itself. The Java language itself is a relatively safe language, and using pure Java code you can’t do things like access data outside array boundaries, transform an object to a type it doesn’t implement, jump to lines of code that don’t exist, and if you do, the compiler will reject compilation. But it has already been said, the class file does not necessarily require in the Java source code to compile, you can use any way to produce, even directly use hex editor to write directly to generate the class files, in the bytecode language level, the Java code can’t do anything can be implemented, at least on semantics can be expressed, A virtual machine that does not check the input byte stream and has full trust in it is likely to crash by loading harmful byte streams. What is the general verification? No more than to verify whether the class file conforms to the class file format constraints (class file has what constraints, see the article class file structure), including the format check is a total of four, respectively is the file format check, metadata check, bytecode verification, symbol reference verification.

It is worth mentioning that the validation phase is a very important but not necessarily necessary phase for the virtual machine class loading mechanism. If all the code you are running has been repeatedly used and validated, you can consider turning off most of the class validation measures during the implementation phase. To shorten the time it takes to load virtual machine classes (this can be turned off with the -xverify: None argument)

To prepare

The preparation phase is the formal allocation of memory for class variables and the setting of their initial values. The memory used by these variables will be allocated in the method area. Two more confusing concepts need to be emphasized at this stage. First, only class variables (static modified variables) are allocated, not instance variables, which will be allocated in the Java heap along with the object when it is instantiated. Second, The initial value referred to here is “normally” the zero value of the data type. For a simple example, suppose a class variable is defined as follows :public static int value = 123; The initial value of the variable after the preparation phase is 0 instead of 123, because no Java methods have been executed yet, and the putStatic instruction that assigns value to 123 is stored in the class constructor <clinit>() method when the program is compiled. So assigning value to 123 will only be performed during initialization.

If a class field has a ConstantValue attribute in the field attribute table, the variable value in the preparation stage will be initialized to the value specified by the ConstantValue attribute. public static final int value = 123; The Javac will generate a ConstantValue attribute for value at compile time, and in preparation the vm will assign value to 123 based on the ConstantValue setting.

parsing

The parsing phase is the process in which the virtual machine replaces symbolic references in the constant pool with direct references. Symbolic references can be seen in the class file structure. It is very common to make multiple parsing requests for the same symbolic reference. Virtual machine implementations can cache the results of the first parsing (recording direct references in the running constant pool and identifying constants as parsed state) to avoid repeating the parsing action. Subsequent reference resolution requests should always succeed regardless of whether multiple resolution actions are actually performed, and similarly, if the first resolution fails, other instructions should receive the same exception for the symbol resolution request.

Symbolic references are independent of the layout implemented by the virtual machine, and the target of the reference does not have to have been loaded into memory. The memory layout of various virtual machine implementations can vary, but the symbolic references they accept must be consistent, because the literal form of symbolic references is explicitly defined in the Class file format of the Java Virtual Machine specification.

A direct reference can be a pointer to a target, a relative offset, or a handle that can be indirectly located to the target. If there is a direct reference, the target of the reference must already exist in memory.

Initialize the

Class initialization stage is the last step in the class loading process. In the previous class loading process, except the user application can participate in the loading stage through the custom loader, the rest of the actions are completely dominated and controlled by the virtual machine. In the initialization stage, the Java program code defined in the class really starts to execute. The initialization phase is the execution of the class constructor < Client > method. The <client> method is a combination of the compiler’s automatic collection of assignment operations for class variables in a class and statements in a static statement block. The virtual machine guarantees that the <client> method of the parent class has been executed before the <client> method is executed. P.s: The compiler may not generate a <client>() method for a class that has no static variable assignments and no static statement blocks.

• Class loaders

Although class loader is used only for the implementation class loading action, but it played a role in Java programs is not limited to class loading stage, for any one class, need to load it by class loaders with the class itself establish its uniqueness in the Java virtual machine, each class loader, have an independent class namespace. This sentence can express more popular it, compare the two classes are equal, only in these two classes are from the same class loader under the premise of load to be meaningful, otherwise, even if these two classes derived from the same class files, by the same virtual machine loading, as long as the load they’re different class loaders, these two classes are not necessarily equal. Equality refers to equals, isInstance(), and the use of the instanceof keyword for object ownership. There are four types of class loading, as follows.

Bootstrap ClassLoader: loads classes in the JAVA_HOME\lib directory or in the path specified by the -xbootclasspath parameter that are recognized by the virtual machine (by filename, such as rt.jar). The Extension ClassLoader is responsible for loading libraries in the JAVA_HOME\lib\ext directory or in the path specified by the java.ext.dirs system variable. The Application ClassLoader is responsible for loading libraries on the user’s classpath. Of course, we can also implement a custom ClassLoader by inheriting java.lang.ClassLoader.

It is worth mentioning that from the perspective of the Java virtual machine, there are only two different class loaders. One is the Bootstrap ClassLoader, which is implemented in C++ language and is part of the virtual machine itself. The other is all the other classloaders, which are implemented in the Java language, independent of the virtual machine, and inherit from the abstract java.lang.classloader class.

The diagram above shows the hierarchical relationship between class loaders, which is the parent delegate model of class loaders. Note that the parent-child relationship between class loaders is generally not implemented as an inherited relationship, but rather uses a composite relationship to duplicate the parent loader code. The logic is similar to the following code.

protected synchronizedClass<? > loadClass(String name,boolean resolve)
        throws ClassNotFoundException {
    // First, check if the class has already been loaded
    Class c = findLoadedClass(name);
    if (c == null) {
        try {
            if(parent ! =null) {
                c = parent.loadClass(name, false);
            } else{ c = findBootstrapClass0(name); }}catch (ClassNotFoundException e) {
            // If still not found, then invoke findClass in order
            // to find the class.c = findClass(name); }}if (resolve) {
        resolveClass(c);
    }
    return c;
}
Copy the code

When a class loader receives a class loading task, it will first hand it to its parent class loader to complete it. Therefore, the final loading task will be passed to the top starting class loader. Only when the parent class loader fails to complete the loading task, it will attempt to execute the loading task. One advantage of using parental delegation is that, for example, loading the java.lang.Object class in the rt.jar package, whichever loader loads the class ends up delegating to the top bootstrap class loader, thus ensuring that different classloaders will end up with the same Object.

Note that the parent delegate model is not a mandatory constraint model, but rather a class loader implementation recommended by the file designer to developers and followed by most class loaders.