Java Virtual machine-class loading mechanism

Source: Internet
Author: User
Tags prepare

"Deep Java Virtual Machine" Four: class loading mechanism

Class loading process

class starts from being loaded into the virtual machine's memory, and its entire lifecycle includes: load, validate, prepare, parse, initialize, use, and unload seven phases, until the memory is unloaded. The order in which they begin is as follows:

The process of loading, validating, preparing, parsing, and initializing five stages of the class load. In these five phases, the order in which the four phases of loading, validating, preparing, and initializing occurs is deterministic, and the parsing phase is not necessarily, which in some cases can begin after the initialization phase, in order to support runtime bindings for the Java language (also become dynamic or late bound). Also note that the stages here are started in order, rather than sequentially or in order , because these phases are usually mixed in a cross-section, often invoking or activating another phase during one phase of execution.

Here is a brief description of the bindings in Java: Binding refers to associating a method's invocation with the class (method body) where the method resides, and for Java, the bindings are divided into static and dynamic bindings:

    • Static bindings: That is, pre-binding. The method is already bound before the program executes, and is implemented by the compiler or other linker. For Java, a simple binding that can be understood as the program's compile time. In Java, only Final,static,private and constructor methods are pre-bound.
    • Dynamic binding: That is, late binding, also called run-time binding. Binds at run time based on the type of the specific object. In Java, almost all methods are late-bound.
The following details the work done at each stage of the class loading process. Load

During the first stage of the load-time class loading process, the virtual machine needs to complete the following three things during the load phase:

1. Get the binary byte stream defined by the fully qualified name of a class.

2. Transform the static storage structure represented by this byte stream into the runtime data structure of the method area.

3. Generate a Java.lang.Class object representing this class in the Java heap as the access entry for the data in the method area.

Note that the binary byte stream in 1th here is not simply taken from the class file, such as it can be obtained from the jar package, obtained from the network (the most typical application is the applet), generated by other files (JSP application), etc.

In contrast to other stages of class loading, the load phase (accurately, the action of getting the binary byte stream of a class during the load phase) is the strongest stage, because developers can either use the system-provided classloader to complete the load or customize their own classloader to complete the load.

When the load phase is complete, the binary byte stream outside the virtual machine is stored in the method area in the format required by the virtual machine, and an object of the Java.lang.Class class is created in the Java heap so that the data in the method area can be accessed through the object.

When it comes to loading, we have to mention the class loader, and the following is a concrete class loader.

Although the ClassLoader is only used to implement the load action of a class, its role in Java programs is far from being limited to the load phase of classes. For any class, it needs to be determined by its class loader and the class itself to be unique in the Java virtual machine, that is, even if two classes originate from the same class file, the two classes must be unequal if the classloader loading them is different. The "equality" here includes the return results of equals (), IsAssignableFrom (), Isinstance (), and so on, which represent the class object, and the result of the decision to use the INSTANCEOF keyword for the object's owning relationship.

In terms of the Java Virtual machine, there are only two different classloader:

    • Start the ClassLoader: it is implemented in C + + (hotspot only, which is the default virtual machine after JDK1.5, there are many other virtual machines that are implemented in the Java language) and are part of the virtual machine itself.
    • All other ClassLoader: These classloader are implemented by the Java language, independent of the virtual machine, and all inherit from the abstract class Java.lang.ClassLoader, which need to be loaded into memory by the startup ClassLoader before they can load other classes.

From the Java Developer's point of view, the class loader can be roughly divided into the following three categories:

    • Start class loader: Bootstrap ClassLoader, same as above. It is responsible for loading the class libraries (such as Rt.jar, all java.* that are stored in the Jdk\jre\lib (JDK represents the JDK's installation directory, the same below), or in the path specified by the-xbootclasspath parameter, and can be recognized by the virtual machine. The classes that begin are loaded by bootstrap ClassLoader). The startup ClassLoader cannot be referenced directly by a Java program.
    • Extension class loader: Extension ClassLoader, which is implemented by Sun.misc.launcher$extclassloader, is responsible for loading the Jdk\jre\lib\ext directory, or all class libraries in the path specified by the JAVA.EXT.DIRS system variable (such as classes beginning with javax.*), developers can use the extension class loader directly.
    • Application ClassLoader: Application ClassLoader, which is implemented by Sun.misc.launcher$appclassloader, is responsible for loading the class specified by the user class path (ClassPath). Developers can use the ClassLoader directly, if the application does not customize its own classloader, typically this is the default class loader in the program.

Applications are loaded with each other by these three kinds of loaders, and if necessary, we can also add a custom class loader. Because the JVM's own ClassLoader only knows how to load the standard Java class file from the local file system, if you write your own classloader, you can do the following:

1) The digital signature is automatically validated before the non-confidence code is executed.

2) dynamically create custom build classes that meet user-specific needs.

3) Obtain Java class from a specific location, such as in a database and in a network.

In fact, when using applets, a specific classloader is used, because Java class needs to be loaded from the network, and the relevant security information is checked, and the application server is mostly using custom ClassLoader technology.

The hierarchical relationships of these kinds of loaders are as follows:

This hierarchical relationship is known as the parent-delegation model of the ClassLoader. We call the class loader above each layer the parent loader of the current layer ClassLoader, and of course, parent-child relationships between them are not implemented through inheritance, but instead use the combination of relationships to repeat the code in the parent loader. The model was introduced during JDK1.2 and is widely used in almost all Java programs, but it is not a mandatory constraint model, but rather a class loader implementation that Java designers recommend to developers.

The workflow of the parent delegation model is that if a classloader receives a request for a class load, it does not attempt to load the class on its own, but instead delegates the request to the parent loader to complete, then up, so that all class load requests should eventually be passed to the top-level startup class loader. The load cannot be completed until the parent loader finds the required class in its search scope, and the child loader tries to load the class on its own.

The obvious benefit of using the parental delegation model to organize the relationship between ClassLoader is that the Java class has a hierarchy of priorities along with its classloader (plainly, the directory in which it resides), which is important to ensure the stable operation of the Java program. For example, class Java.lang.Object classes are stored in jdk\jre\ Lib under the Rt.jar, so no matter which class loader to load this class, will eventually be delegated to the startup ClassLoader to load, this way to ensure that the object class in the program in the various class loaders are the same class.

Verify

The purpose of the validation is to ensure that the byte stream in the class file contains information that conforms to the requirements of the current virtual machine and does not compromise the security of the virtual machine itself. Implementations of different virtual machines for class validation may vary, but the following four phases of validation are generally performed: file format validation, metadata validation, bytecode validation, and symbolic reference validation.

    • File format validation: Verify that the byte stream conforms to the class file format specification, and can be processed by the current version of the virtual machine, the main purpose of this verification is to ensure that the input byte stream can be correctly parsed and stored in the method area. After this phase of validation, the byte stream is stored in the memory's method area, and the subsequent three validations are based on the storage structure of the method area.
    • Metadata validation: Semantic validation of the metadata information of a class (in fact, the syntax check of each data type in the Class), ensuring that there is no metadata information that does not conform to the Java syntax specification.
    • Bytecode verification: The main task of this phase verification is to conduct data flow and control flow analysis, to verify the method body of the class, to ensure that the method of the class being validated will not make the behavior that endangers the virtual machine security at runtime.
    • Symbol Reference Validation: This is the last stage of validation, which occurs when a virtual machine converts a symbolic reference to a direct reference (the conversion occurs during the parsing phase, which is explained later), and is primarily a matching check for information other than the class itself (a variety of symbol references in a constant pool).

Get ready

The prep phase is a phase that formally allocates memory for class variables and sets the initial value of class variables, which are allocated in the method area. There are a few things to note about this phase:

1. This time memory allocation includes only class variables (static), not instance variables, and instance variables are allocated to the Java heap as objects are instantiated when the object is instanced.

2, the initial value set here is usually the default value of the data type 0 (such as 0, 0L, NULL, FALSE, etc.), rather than being explicitly assigned in the Java code value.

Suppose a class variable is defined as:

public static int value = 3;

Then the initial value of the variable value after the prep phase is 0, not 3, because no Java method has started executing, and the putstatic instruction that assigns value 3 is stored in the class constructor <clinit> () method after the program is compiled. So an action that assigns value to 3 will not be executed until the initialization stage.

The following table lists all the basic data types in Java and the default 0 values for reference types:

Here are a few more points to note:

    • For basic data types, for class variables (static) and global variables, which are used directly if they are not explicitly assigned to them, the system assigns them a default value of 0, and for local variables, they must be explicitly assigned before they are used, otherwise the compilation does not pass.
    • Constants that are both static and final modified must be explicitly assigned at the time of declaration, or not at compile time, whereas only final-modified constants can be explicitly assigned at the time of declaration, or they can be explicitly assigned when the class is initialized, in summary, You must assign a value to it explicitly before you use it, and the system does not give it a default value of 0.
    • For reference data type reference, such as an array reference, an object reference, and so on, if it is not explicitly assigned and is used directly, the system assigns it a default value of 0, or null.
    • If no values are assigned to elements in the array at initialization, the elements will be given a default value of 0 based on the corresponding data type.

3. If the Constantvalue attribute exists in the field attribute table of the class field, which is both final and static, then in the prepare phase the variable value is initialized to the value specified by the Constvalue property.

Assume that the above class variable value is defined as:

public static final int value = 3;

Compile-time Javac will generate the Constantvalue property for value, and in the prepare phase the virtual machine will assign value to 3 based on the settings of Constantvalue. This is the case when you recall the 2nd example of a passive reference to an object in the previous blog post . We can understand that the static final constant puts its result in the constant pool of the class that called it at compile time.

The parsing phase is the process by which a virtual machine converts a symbolic reference in a constant pool into a direct reference. In Class file StructureThe differences and associations between symbolic references and direct references have been compared in the article and are not described here.    It is said that the parsing phase may start before initialization, or it may start after initialization, and the virtual opportunity is judged by the need to parse the symbolic reference in the constant pool (before initialization) when the class is loaded, or until a symbolic reference is to be used before parsing it (after initialization).    A common thing to do with multiple parsing requests for the same symbol reference is that the virtual machine implementation may cache the results of the first parse (direct references are recorded in the run-time pool, and the constants are marked as resolved), thus avoiding repetitive parsing actions. Parsing action is mainly for class or interface, field, class method, interface method Four kinds of symbol reference, corresponding to the constant pool of constant_class_info, Constant_fieldref_info, Constant_methodref_info, Constant_interfacemethodref_info four types of constants. 1. Parsing of classes or interfaces:Determine whether the direct reference you want to convert is an array type or a reference to an ordinary object type for different parsing. 2. Field parsing:When a field is parsed, it finds in this class whether it contains a field with a simple name and a field descriptor that matches the target, and if so, the end of the search, and if not, the individual interfaces and their parent interfaces implemented by the class are searched recursively from top to bottom by inheritance. The parent class is searched recursively from top to bottom by inheritance, until the lookup is over, and the lookup process looks like this: The Search order for field resolution is easily seen from the execution results of the following code: [Java]View PlainCopy
  1. Class super{
  2. public static int m = 11;
  3. static{
  4. System.out.println ("Executed super class static statement block");
  5. }
  6. }
  7. Class Father extends super{
  8. public static int m = 33;
  9. static{
  10. System.out.println ("Execution of the parent static statement block");
  11. }
  12. }
  13. Class child extends father{
  14. static{
  15. System.out.println ("Execution of a subclass static statement block");
  16. }
  17. }
  18. Public class statictest{
  19. public static void Main (string[] args) {
  20. System.out.println (CHILD.M);
  21. }
  22. }
The execution results are as follows: Super class static statement block executed
The parent class static statement block was executed
33
If you comment out the row defined in the Father class for M, the output is as follows: Super class static statement block executed
11 In addition, it is clear that this is Previous blog postIn the case of the 1th example, here we can analyze the following: Static variable occurs in the static parsing phase, that is, before the initialization, the field's symbolic reference has been converted to a memory reference, it is also associated with the corresponding class, because in the subclass is not found to match the M-matched field,    Then M will not be associated with subclasses, so initialization of subclasses is not triggered. Finally, it is important to note that in theory the search is parsed in the order described above, but in practice, the compiler implementation of the virtual machine may be more stringent than the above specification requirements. If a field with the same name appears in both the interface and the parent class of the class, or if it appears in both the interface of itself or the parent class, the compiler may refuse to compile. If you make some changes to the above code, change super to interface, and the child class to inherit the Father class and implement the Super interface, the following error will be reported at compile time: statictest.java:24: The reference to M is ambiguous, the variable m in Father and The variable m in Super
are matched
System.out.println (CHILD.M);
^
1 Error 3, class method analysis:The parsing of the class method is similar to the search step for the field resolution, just a lot of steps to determine whether the method is in a class or an interface, and a matching search for a class method is to search for the parent class and then search the interface. 4, interface method analysis:Like the class method parsing step, the knowledge interface does not have a parent class, so it is only a recursive search for the parent interface.
     Initialization      initialization is the last step in the class loading process, and at this stage, the Java program code defined in the class is really started. In the preparation phase, the class variable has been assigned the initial value of the system requirement, while in the initialization phase, the class variables and other resources are initialized according to the subjective plan specified by the programmer, or can be expressed from another angle: the initialization stage is the execution class constructor <clinit> () The process of the method.     Here is a brief description of the following <clinit> () method Execution rules:    1, <clinit> The () method is generated by the assignment of all class variables in the compiler's automatic collection of classes and statements in a static statement block, the order in which the compiler collects is determined by the order in which the statements appear in the source file, and the static statement block can only access variables defined before the static statement block, the variables defined behind it, You can assign a value in the preceding static statement, but you cannot access it. The     2, <clinit> () method differs from the instance constructor <init> () method (the class's constructor), which does not require the explicit invocation of the parent class constructor, and the virtual opportunity guarantees that the subclass of the <clinit> () the <clinit> () method of the parent class has been executed before the method executes. Therefore, the class of the first executed <clinit> () method in the virtual machine is definitely java.lang.Object. The     3, <clinit> () method is not necessary for a class or interface, and if there is no static statement block in a class and no assignment to the class variable, the compiler can not generate a <clinit> () method for the class.     4, the interface cannot use static statement blocks, but there are still class variable (final static) initialization of the assignment operation, so the interface and class will generate the <clinit> () method. But the interface fish is different: the <clinit> () method that executes the interface does not need to perform the parent interface's <clinit> () method first, and the parent interface is initialized only if the variables defined in the parent interface are used. In addition, the implementation class of the interface does not execute the <clinit> () method of the interface at initialization time.     5, virtual opportunity to ensure that a class of <clinit> () method in a multithreaded environment is correctly locked and synchronized, if more than one thread to initialize a class at the same time,Then there will only be one thread executing the <clinit> () method of this class, and the other threads will need to block the wait until the active thread executes the <clinit> () method. If there is a long-time operation in the <clinit> () method of a class, it can cause multiple threads to block, and in practice the blocking is often very covert.      A simple example is given below to illustrate the above rule more clearly: [Java]View PlainCopy
  1. Class father{
  2. public static int a = 1;
  3. static{
  4. A = 2;
  5. }
  6. }
  7. Class child extends father{
  8. public static int b = A;
  9. }
  10. Public class clinittest{
  11. public static void Main (string[] args) {
  12. System.out.println (CHILD.B);
  13. }
  14. }
Executing the above code will print 2, which means that the value of B is assigned to 2. Let's look at the steps to get the result. First, allocate memory for class variables and set class variable initial values at the prep stage so that both A and B are assigned the default value of 0, and then give them the value specified in the program when the <clinit> () method is called. When we call child.b, the <clinit> () method of the child is triggered, according to Rule 2, before the <clinit> () method of its parent class father is executed, and according to Rule 1, the execution of the <clinit> () method, the associated static statement needs to be executed in the order in which the static statement or static variable assignment operation occurs in the code, so when the <clinit> () method that executes father is triggered, a is assigned a value of 1.    Execute the statement in the static statement block, assign a value of 2, and then execute the child class's <clinit> () method, which assigns the value of B to 2. If we reverse the Father class, "public static int a = 1;" Statement and the order of the "static statement block", the program executes, then prints out 1. It is clear that, according to Rule 1, when the Father <clinit> () method is executed, the contents of the static statement block are executed first, followed by "public static int a = 1;"    Statement. In addition, after the order of the two is reversed, if a is accessed in a static statement block (such as assigning A to a variable), it will be an error at compile time because it can only assign a value to a and cannot be accessed according to Rule 1.


Summing up the entire class loading process, except in the load phase of the user application can customize the class loader participation, all the rest of the action is completely dominated and controlled by the virtual machine. The Java program code (and bytecode) defined in the class is started before initialization, but the execution code here is only the beginning and it is limited to the <clinit> () method. The class loading process is mainly to load the class file (exactly, the binary byte stream of the classes) into the virtual machine memory and actually execute the bytecode operation before it really starts after the load is complete.

Java Virtual Machine-class loading mechanism

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.