The Java virtual machine loads the data describing the Class from the Class file to the memory, verifies, transforms, and initializes the data, and finally forms Java types that can be directly used by the VIRTUAL machine. This process is called the virtual machine Class loading mechanism. Unlike other languages that do concatenation at compile time, the Java language loads, concatenates, and initializes types while the program is running. This strategy makes early compilation difficult for the Java language and adds a slight performance overhead to class loading. However, it provides high extensibility and flexibility for Java applications. Java naturally can be dynamically extended language features are based on the runtime dynamic loading and dynamic connection characteristics. For example, writing an interface-oriented application can wait until run time to specify its actual implementation classes, and a user can have a native application load a binary stream at run time from the network or elsewhere as part of its program code through Java preset or custom classloaders. This dynamic assembly method has been widely used in Java programs, from the most basic applets, JSPS to the relatively complex OSGi technology, all rely on the Java language runtime class loading to be born.
The timing of class V loading
When a type is loaded into vm memory and unloaded from memory, Its entire life cycle will go through Loading, Verification, Preparation, Resolution, and initialization Initialization, Using and Unloading. The three parts of Initialization, preparation and parsing are collectively known as Linking. The diagram below:
As shown above, the loading, validation, preparation, initialization, and unloading phases are in a definite order, and the loading process of a type must begin in this order, while the parsing phase may not: It can be started after the initialization phase in some cases to support the Runtime binding feature of the Java language (also known as dynamic binding or late binding). Note that I write “start”, not “proceed” or “finish”, because these phases are often intermingled with each other, invoking and activating another phase during the execution of one.
The Java Virtual Machine Specification does not enforce the conditions under which to begin the first phase of the classloading process, “loading,” and leaves this up to the implementation of the virtual machine. But for the initialization phase, the Java Virtual Machine Specification specifies that there are only six cases in which classes must be “initialized” immediately (and loading, validation, and preparation naturally need to begin before that) :
(1) When encountering the four bytecode instructions of New, getstatic, putstatic or Invokestatic, if the type has not been initialized, it needs to trigger its initialization phase first. Typical Java code scenarios that can generate these four instructions are:
- When an object is instantiated using the new keyword.
- Read or set a static field of a type (except for static fields that are modified by final and have been put into the constant pool at compile time).
- When a static method of a type is called.
(2) When a reflection call is made to a type using a java.lang.reflect method, if the type has not already been initialized, it needs to be initialized 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 primary class (the one containing the main() method) to execute. The virtual machine initializes this primary class first.
(5) When using the new dynamic language support in JDK 7, If a Java lang. Invoke. The final solution analysis results for MethodHandle instance REF_getStatic, REF_putStatic, REF_invokeStatic, REF_newInvokeSpecial handle four kinds of methods, If the class corresponding to the method handle has not been initialized, it needs to be initialized first.
(6) When an interface defines a new default method in JDK 8, if any of the interface’s implementation classes are initialized, the interface should be initialized before it.
The Java Virtual Machine Specification uses a very strong qualifier “have and only” for each of the six scenarios that trigger the initialization of a type, and the action in these six scenarios is called an active reference to a type. In addition, methods of all reference types do not trigger initialization, called passive references. Here are three examples of what a passive reference is, as shown in Listing 7-1, Substitution listing 7-2, and Listing 7-3.
Listing 7-1 shows an example of a passive reference
After the above code is run, only “super-class init.” will be output, not “node-class init.”. For static fields, only the class that directly defines the field is initialized, so referring to a static field defined in a parent class by its subclass triggers initialization of the parent class but not the subclass. Whether to trigger the loading and validation phase of subclasses is not specified in the Java Virtual Machine Specification, so it depends on the implementation of the virtual machine. In the case of the HotSpot virtual machine, you can observe that this operation causes subclass loading with the -xx: +TraceClassLoading parameter.
Listing 7-2 shows the second example of a passive reference
This code reuse the code listing 7-1 in the SuperClass, found no output after running “Super – Class init.”, that not trigger type of com. Toutou. Sample. The book. The SuperClass initialization phase. But this code inside triggered another called “[com. Toutou. Sample. Book. The SuperClass” class initialization phase, for the user code, this is not a valid type name, It is a subclass automatically generated by the virtual machine that directly inherits from Java.lang. Object, and the creation action is triggered by the bytecode instruction Newarray.
This class represents an element type for the com. Toutou. Sample. The book. The SuperClass of a one-dimensional array, the array of its properties and methods (the user can directly use only be modified for the length attribute of public and clone () method) are implemented in this class. Access to arrays in Java is safer than in C/C++, largely because this class wraps access to array elements [1], whereas in C/C++ it translates directly to the movement of array Pointers. In the Java language, thrown when check to an array of Java. Lang. ArrayIndexOutOfBoundsException abnormalities, avoids the direct cause for illegal memory access.
Listing 7-3 shows the third example of a passive reference
This code does not output “const-class init.” This is because although the Java source code does reference the ConstClass constant HELLO, it is optimized by constant propagation at compile time. The value of this constant “hello world.” is stored directly in the constant pool of the NotInitialization class. These are actually converted to references to the NotInitialization class’s own constant pool. That is, there is no reference point to a ConstClass in the NotInitialization Class file, and the two classes are no longer related to each other once they are translated into the Class file.
The loading process of an interface is slightly different from the loading process of a class. Some special instructions are required for the interface: As with classes, interfaces also have an initialization process. The above code uses static{} to output initialization information. Interfaces cannot use static{}, but the compiler still generates a “()” class constructor [2] for the interface, which initializes member variables defined in the interface. The real difference between an interface and a class is the third of the six “have and only” initialization scenarios described earlier: When a class is initialized, all of its parents are required to be initialized. However, when an interface is initialized, its parents are not required to be initialized. The parent interface is initialized only when the parent interface is actually used (for example, referencing constants defined in the interface).
V class loading process
Next, we’ll take a closer look at the entire process of class loading in a Java virtual machine, namely the specific actions performed in the five phases of load, validation, preparation, parsing, and initialization.
2.1 loading ♛
The “Loading” stage is a part of the whole “Class Loading” process, and I hope you didn’t confuse these two similar-looking terms. During the load phase, the Java virtual machine needs to do three things:
1) Get the binary byte stream that defines a class by its fully qualified name.
2) Convert the static storage structure represented by this byte stream to the runtime data structure of the method area.
3) Generate a java.lang.Class object representing the Class in memory as an access point to the various data of the Class in the method area.
The Java Virtual Machine Specification is not particularly specific about these three requirements, leaving much flexibility for virtual machine implementation and Java applications. For example, the rule “get the binary byte stream that defines a Class by its fully qualified name” does not specify that the binary byte stream must be obtained from a Class file, or exactly where or how to get it at all. With this gap alone, Java virtual machine users can set up a fairly wide open arena during the loading phase, where creative developers play a variety of tricks during the Java development calendar, and on which many of the most important Java technologies are built, such as:
- Reading from ZIP packages was common and eventually became the basis for future JAR, EAR, and WAR formats.
- The most typical application in this scenario is a Web Applet.
- Runtime compute generation, the scenario that is most commonly used is the dynamic Proxy technique. In java.lang.reflection.proxy, Is to use the ProxyGenerator. GenerateProxyClass () for the specific interface generated form of Proxy class for “* $Proxy” binary byte streams.
- Generated by other files, the typical scenario is JSP applications, JSP files generate the corresponding Class files.
- Reading from a database is a relatively rare scenario, for example some middleware servers (such as SAP Netweaver) can choose to install programs into a database to distribute program code across clusters.
- It can be obtained from encrypted files. This is a typical protection against Class file decompilation. It decrypts the Class file at load time to protect the program running logic from snooping.
- .
Compared to other phases of the class loading process, the loading phase of non-array types (specifically, the action of the loading phase to retrieve the binary byte stream of the class) is the most controlled phase for the developer. The loading phase can be done either using the bootloader built into the Java virtual machine, or by user-defined class loaders. Developers define their own class loaders to control how byte streams are retrieved (overriding a classloader’s findClass() or loadClass() method). The implementation wants to give the application the dynamics to get the running code.
The situation is different for array classes, which themselves are not created by the classloader, but are dynamically constructed directly in memory by the Java virtual machine. However, array classes and class loaders are still closely related, because the ElementType of an array class is ultimately loaded by the class loader. The creation of an array class (called C for short) follows these rules:
- If the Component Type of the array is a reference Type, the loading procedure defined in this section is used recursively to load the Component Type. Array C will be identified on the class namespace of the class loader that loads the component type. (This is important, as we’ll see in Section 7.4, because a type must be unique with the class loader.)
- If the array’s component type is not a reference type (for example, int[] is int), the Java virtual machine will mark array C as associated with the bootstrap classloader.
- The accessibility of an array class is the same as the accessibility of its component type. If the component type is not a reference type, the accessibility of its array class defaults to public and is accessible by all classes and interfaces.
After the loading phase is complete, the external binary byte streams of the Java VM are stored in the method area in the format set by the VM. The data storage format in the method area is fully defined by the VM. The Java Virtual Machine Specification does not specify the specific data structure in this area. Once the type data is properly placed in the method area, an object of the Java.lang. Class Class is instantiated in the Java heap memory, which acts as an external interface for the program to access the type data in the method area.
Stages of loading and the connection part of the action, such as part of the bytecode file format validation action) was performed by cross loading phase is not yet complete, may have begun connection phase, but the clip in the middle of the stage of loading action, still belongs to the part of connection phase, the two stages of start time remains fixed order.
V Blog Summary
Special disclaimer: This article is not intended for commercial use and is intended only for reading notes. Most of the content (with the occasional addition of some bloggers’ impressions) is excerpted from “Understanding Java Virtual Machines in Depth (3rd edition)”. To read the original, please purchase the original book.