As we all know, our Java program is compiled into a class file by the compiler. All the information described in the class file needs to be loaded into the VIRTUAL machine memory to run and use. So how does the virtual machine load these class files? What does the virtual machine do in the process of loading the class file? Today we will decipher the class loading mechanism of virtual machines.
The virtual machine loads the class file into memory, verifies, parses, and initializes the data, and finally forms Java types (class objects) that can be directly used by virtual machines. This is the class loading mechanism of virtual machines.
The entire life cycle of a class includes seven stages: loading, validation, preparation, parsing, initialization, use, and unloading. , the three stages of verification, preparation and parsing are collectively referred to as the connection stage. As shown in figure:
The first five phases are the class loading process. The order of the load, validate, prepare, and initialize phases is fixed, while the parse phase is not, and in some cases it can happen after the initialization phase. So what does the virtual machine do at each step of class loading?
loading
Loading is the first stage of the class loading process, during which the virtual machine does three things:
1. Get the binary byte stream that defines the class by its fully qualified name. Simply put, it is a process of locating resources by adding the package name of a class to the class name.
2. Convert the static storage structure represented by this byte stream into the runtime data structure of the method area. That is, static variables, constants, and other information defined in a class are stored in a method area.
3. Generate a java.lang.Class object representing this Class in the heap memory as an access point for the various data of this Class in the method area.
To sum up, the main work in the loading stage is to load the class binary file into memory, store the static variables, constants, class information and other data defined in the class into the method area, and create a class object representing this class in the heap memory, as the access to the data information of this class in the method area. The programmer can hold this Class object.
validation
Verification is the first step in the connection phase. The purpose of the verification phase is to ensure that the information contained in the class file meets the requirements of the VIRTUAL machine and does not compromise the security of the virtual machine. The verification mainly includes the following aspects:
1. File format verification. The main purpose is to ensure that the input byte stream is properly parsed and stored in the method area in a format that conforms to the requirements of a Java type message. Verification in this stage is based on binary byte streams. Only after this stage is verified, the byte streams can be stored in the method area. All the following three stages of verification are based on the storage structure of the method area and do not directly manipulate the byte streams.
2. Metadata validation. The main purpose of this phase is to semantically verify the metadata information of the class (the data that defines the data) to ensure that there is no metadata information that does not conform to the Java language specification. Whether the class has a parent class, whether the parent class of the class inherits classes that are not allowed to be inherited, and whether the fields and methods in the class conflict with the parent class.
3. Bytecode verification. The purpose is to determine whether the program semantics are legal and logical through data flow and control flow analysis. After verifying the data types of metadata information in phase 2, the method body of the class is analyzed to ensure that the methods of the verified class do not harm VM security when running.
4. Symbol reference validation. Symbolic reference validation occurs when the virtual machine converts symbolic references to direct references during the parse phase, the third phase of the connection. The purpose of symbolic reference validation is to ensure that the parsing action executes properly.
The validation phase is a very important, but not necessarily necessary, phase for class loading mechanisms. If all the code you are running has been used and validated over and over again, you can use the virtual machine parameter -xVerify: None to turn off most of the class validation to shorten the class load time.
To prepare
The main work of the preparation phase is to allocate memory for the static variables of the class and set the initial default values for the variables. The memory used by these variables is allocated in the method area. There are two points to note here:
1. Only static variables are allocated in this phase, not instance variables (static variables are common to all objects; instance variables are private to objects). Instance variables are allocated in the Java heap along with the object when it is instantiated.
2. Assigning an initial value to an object is the zero value corresponding to each data type. Suppose we have a static variable defined as public static int a = 1; So the initial value of variable A is 0 instead of 1, and the initial value 1 is assigned to variable A during initialization. The initial default value is null for reference types.
parsing
The parsing phase is the process by which the virtual machine replaces symbolic references in the constant pool with direct references.
Symbolic reference: A symbolic reference is a set of symbols that describe the referenced object. Symbols can be used to refer to any literal as long as they are used unambiguously to the target. The literal form of a symbol reference is clearly defined in the Class file format of the Java Virtual Machine specification.
Direct reference: A direct reference can be a pointer to a target, a relative offset, or a handle that can be indirectly located to the target. A direct reference is related to the memory layout implemented by the VIRTUAL machine. The translation of a symbolic reference on different virtual machine instances will not be the same. If there is a direct reference, the target of the reference must already exist in memory.
The parse action is mainly for symbolic references to classes or interfaces, fields, static methods, interface methods, method types, method handles, and call point qualifiers.
Initialize the
The initialization phase is the last step in the class loading process. In the previous class loading process, except the loading stage we can participate in through the custom class loader, the rest stages are automatically completed by the VIRTUAL machine. In the initialization phase, we actually start executing the Java code defined in our program. The main task of the initialization phase is to assign the static variables of the class to the initial values specified in our program. This is what we saw in the preparation phase above, where the value of variable A goes from 0 to 1. At this stage, our program specifies the initial value in two ways:
1. Explicit copy when declaring static variables. Public static int a = 1; It assigns 1 to the variable A during initialization.
2. Assign values via static code blocks. Static {a = 2}; The initial value of variable A is assigned to 2.
The order of assignment in these two ways is determined by the order in which statements appear in the source file.
This is how the Java Virtual Machine class loading mechanism works and what the virtual machine does at each stage.
Parental delegation model
As mentioned earlier, in the whole process of class loading, except for the loading stage we can participate in the custom class loader, the other stages are completed by the virtual machine for us. The virtual machine design team implements this action outside of the Java virtual machine in a code module called the class loader. The goal is to let the application decide how to get the classes it needs.
In addition to defining our own class loaders, the Java Virtual machine also provides us with our own class loaders. It can be divided into the following three categories:
Bootstrap ClassLoader: this ClassLoader is responsible for loading classes stored in the <JAVA_HOME>\lib directory or in the path specified by the -xbootclasspath parameter.
Extension ClassLoader: This loader is responsible for loading all libraries in the <JAVA_HOME>\lib\ext directory, or in the path specified by the java.ext.dirs system variable.
Application ClassLoader: This is responsible for loading the libraries specified on the user-set ClassPath path. If your application does not have a custom class loader, this is generally the default class loader for your application.
All of our applications are loaded by a combination of these three types of loaders, and you can define your own class loaders if necessary. The relationship between these classloaders is as follows:
The hierarchical relationship between class loaders shown in the figure above is called the parent delegate model of class loaders. The parent delegate model requires that all class loaders have their own parent class loaders except for the top-level root class loader. The working process of the parental delegation model is: If a classloader receives a classloading request, it first does not attempt to load the class itself. Instead, it delegates the request to the parent classloader. This is true at every level of classloaders, so all classloading requests should eventually be passed to the root classloader. Only when the parent loader reports that it cannot complete the load request (the desired class is not found in the search scope) will the child loader attempt to load it itself.
Class loaders are only used to load classes, but they do more than just load classes in Java programs. For any class, its uniqueness within the Java virtual machine needs to be established both by the classloader that loads it and by the class itself. Simply put, a class whose class file is loaded by two different classloaders is not “equal” or the same class.
One of the obvious benefits of using the parent delegate model is that Java classes have a hierarchical relationship with priority along with their classloaders. For example, the java.lang.Object class, which is stored in rt.jar, is eventually delegated to the root class loader at the top of the model whenever it is loaded, so Object is the same class (always loaded by the root class loader) in every class loader environment of the program. On the other hand, instead of using the parent delegate model and having each class loader load it, if the user wrote a class called java.lang.Object and put it in the ClassPath, the system would have multiple Object classes and the application would be cluttered.
The above content summarizes the entire process of Java class loading mechanism and the principle of parental delegation model, welcome to exchange.
Three things to watch ❤️
If you find this article helpful, I’d like to invite you to do three small favors for me:
-
Like, forward, have your “like and comment”, is the motivation of my creation.
-
Follow the public account “Java rotten pigskin” and share original knowledge from time to time.
-
Also look forward to the follow-up article ing🚀
-
[666] Scan the code to obtain the learning materials package
Author: Wind program ape
Source: www.cnblogs.com/fangfuhai/p…