The virtual machine Class loading mechanism 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 VMS.

It is important to note that the Java language is different from other compile-time languages in that the wiring is done at runtime, which adds a slight performance overhead at class-loading time, but provides a high degree of flexibility for Java applications. For example, if you write an application that uses an interface, you can wait until runtime to specify its actual implementation.

Through the article, you can learn the following

Class loading time class loading process class loader and defects

1. Class loading timing

From the moment a class is loaded into vm memory, to the moment it is unloaded from memory, there are seven stages in its life cycle:

Load, validate, prepare, parse, initialize, use, unload

The load, validate, prepare, initialize, and unload phases are determined, and because Java supports runtime binding, parsing can in some cases be done after the initialization phase

The virtual machine specification states that there are only four cases in which classes must be “initialized” immediately (loading, validating, and preparing naturally come before that)

The four bytecode instructions, new, getstatic, putstatic, or Invokestatic, are encountered in common scenarios: When you use the new keyword to instantiate an object to read or set a class’s static fields (except those that are modified by final and have put the result into the constant pool at compile time) and call a class’s static methods to make a reflection call to a class using the java.lang.Reflect package’s methods If the parent of a class is not initialized, the initialization of its parent must be triggered first. When a VM starts, the user needs to specify a primary class (the one containing the mian() method) to execute. The VM initializes the primary class first

2. Class loading process

Take a look at the whole process of class loading, loading, validating, preparing, parsing, and initializing

1. The load

The loading process needs to do three things

Use the fully qualified name of a Class to get the binary byte stream that defines that Class. Convert the static storage structure represented by the byte stream to the runtime data structure of the method area. Generate a Java.lang. Class object in the Java heap that represents that Class as an entry point to the method area for that data. The binary byte stream for point 1 can be obtained from files, networks, databases, etc. The loading phase can also be done by user-defined class loaders; Parts of the load and join (file format verification) are crossed

2. Verify

The purpose of verification is to ensure that the information contained in the byte stream of the Class file meets the requirements of the current VM and does not compromise vm security

(1) File format verification:

Start with magic number 0xCAFEBABE Major and minor version number Within the scope of the current VM Constant pool Constants are supported by unsupported constant types (check constant tag) Any index value that points to a constant refers to a nonexistent constant or does not match a constant type CONSTANT_Utf8_info does not conform to the UTF8 encoding of the data Class file parts and the file itself is deleted or additional information (2)

Does the class have a parent (all classes except java.lang.Object should have a parent) Does the parent of the class inherit from a class that is not allowed to inherit (a class modified by final)? Whether the fields in all method classes required by the parent class or interface are implemented, and whether the methods are in conflict with the parent class (for example, the final fields of the parent class are overwritten, or method overloading occurs that does not conform to the rules, for example, method parameters are all the same, but return value types are different, etc.)

Ensure that the data type of the operand stack and the sequence of instructions work together at any time, for example: Put an int on the stack and load it into the local table as long to make sure that the jump instruction doesn’t jump to any bytecode instruction outside the method body and that the type conversion in the method body is valid, For example, it is dangerous and illegal to assign a subclass object to a completely unrelated data type that has no inheritance relationship:

Whether a fully qualified name described by a string in a symbol reference can be found corresponding to a class whether a field descriptor for a method exists in the specified class and whether the access of classes, fields, and methods described by a simple name in a symbol reference is accessible to the current class

3. Prepare

The preparation phase is to formally allocate memory for class variables and set their initial values. Memory allocation includes only class variables (static modified variables), not instance variables, and initialization represents zero, i.e. static variables are initialized to 0

4. The 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 describes the referenced object as a set of symbols, which can be any literal, as long as it is used to unambiguously locate the object. Symbolic references are independent of the memory layout implemented by the virtual machine, and the referenced target does not have to have been loaded into memory for a direct reference: a direct reference can be a pointer directly to the target, a relative offset, or a sentence handle that can be indirectly located to the target. Direct references are related to the memory layout implemented by VMS. Direct references translated from the same symbol reference on different VM instances are generally different. The resolution action is mainly for class or interface resolution, field resolution, class method resolution, and interface method resolution

5. The initialization

The initialization phase is to initialize other resources of class variables according to the subjective plan made by the programmer through the program. Or the process of executing the class constructor ().

() method is by the compiler automatically collect all class variable assignment in class action and static blocks (static {}) of the statement and, produced by the compiler collection order is decided by the order of the statement in the source file, static block can only access to the definition in the static block variables before and after its variables, You can assign values in the previous static block, but you can’t access them. Unlike class constructors (or instance constructors () methods), the () method does not require an explicit call to the superclass constructor, and the virtual machine guarantees that the superclass’s () method will be executed before the subclass’s () method breaks the law. Therefore, the first () offending class to be executed in a virtual machine must be java.lang.object. Since the () law of the parent class is executed first, that means that the static block defined in the parent class takes precedence over the variable assignment () method of the child class is not necessary for the class or interface. If a class clock has no static block and no assignment to the variable, The compiler can then not produce methods for this class and can’t use static blocks in the interface, but there’s still assignment for variable initialization, so the interface generates methods just like the class does. But interfaces, unlike classes, execute the interface’s () method without first executing the parent interface’s () method. The parent interface is initialized only when a variable defined in the parent interface is used. In addition, the implementation of the interface class in initialization time won’t perform interface () method () method of the virtual opportunity to ensure that a class in a multithreaded environment is properly locked and synchronization, if multiple threads at the same time to initialize a class, so there will be only one thread to execute () method in this class, other threads need to be blocked waiting, Until the active thread finishes executing the () method.

3. Class loaders and their shortcomings

Class loaders are only used to load classes, but for any class, the uniqueness of the Java virtual machine must be determined by the class loader and the class itself.

Parental delegation model

A virtual machine has two different class loaders. One is the Bootstrap ClassLoader, which is implemented in C++ and is part of the virtual machine. The other is all the other class loaders, which are implemented in the Java language, independent of the virtual machine, and all inherit from the abstract java.lang.classloader class

Bootstrap ClassLoader: Load a virtual machine recognized class library in the <JAVA_HOME>\lib directory or in the path specified by the -xbootCLASspath parameter into the virtual machine memory. The Launcher ClassLoader cannot be directly referenced by a Java program: the Extension ClassLoader is called sun.misc.LauncherAppClassLoader is responsible for loading libraries specified on the user’s classpath. Our applications are loaded by these three types of loaders, and if necessary, you can add your own class loaders

The working process of the parental delegation model is: If a classloader receives a classload 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 load requests should eventually be passed to the top level of the starting classloader. Only when the parent loader reports that it cannot complete the load request (it did not find the required class in its search scope) does the child loader tell who to load it

Benefits: Java classes have a hierarchy of priorities along with their classloaders. The java.lang.Object class, for example, is stored in rt.jar. No matter which class loader loads the class, the loader is eventually started to complete the loading, so the Object class is the same class in the program’s various class loader environment. On the other hand, if there was no parent delegate model and each class loader loaded it by itself, if the user wrote a class named java.lang.Object and put it in the ClassPath, the system would have multiple Object classes, causing confusion

Break the parent delegate model

The parent delegate model is the way Java designers recommend class adder implementations to developers, but there have been three major break-ins

The parent delegate model was introduced after JDK1.2, but classloaders and abstract java.lang.ClassLoader existed in JDK 1.0. Java designers had to make some compromises when introducing the parental delegation model. A new protected method, findClass(), was added to java.lang.classloader after JDK 1.2 for forward compatibility. The only reason for the user to inherit java.lang.classloader is to override the loadClass() method, because the virtual machine calls the private loadClassInternal() method of the loader during class loading. The only logic for this method is to call its own loadClass().

After JDK1.2, users are discouraged from overwriting the loadClass() method because the loadClass() method is a parent-delegated implementation and a protected findClass() method is provided. In the loadClass() method logic, if the parent class fails to load, it calls its own findClass() method to complete the load

The thread context class loader Parents delegation model solves each kind of loader problem of the unity of the base class, but if the base class to call back to user code, is can’t do, because a base class of the class loader to load the directory of the file, but the base class call to the user’s code, base class class loader cannot load the user directory of the class

To solve this dilemma, the Java design team introduced a less elegant design: the Thread Context ClassLoader. This classloader can be set through the setContextClassLoader() method of the java.lang.Thread class. If it has not been set when the Thread is created, it will inherit one from the parent Thread; The class loader is an application class loader if it is not set at the global scope of the application.

With thread-context class loaders, base classes (such as rt.jar) can load user classes only through their own startup class loaders. You can now actively obtain the thread-context class loader to complete the user’s class loading. For Service Provider Interface (SPI) Service provision apis, thread context class loaders such as JNDI, JDBC, JCE, JAXB, and JBI can be used

OGSi OGSi is the current industry’s “de facto” Java modularity standard, and OSGi’s implementation of its custom class loader mechanism is the key to implementing modular hot deployment. Each program module (called a Bundle in OSGi) has its own class loader. When a Bundle needs to be replaced, the Bundle is replaced with the same class loader to achieve hot replacement of the code

In the OGSi environment, class loaders are no longer the tree structure in the parent delegate model, but are further developed into a network structure. When class loading requests are made, OSGi will search in the following order:

(1) Delegate classes starting with Java.* to the parent class loader

(2) Otherwise, delegate the classes in the list to the parent class loader

(3) Otherwise, delegate the classes from the Import list to the class loader of the Export class Bundle

(4) Otherwise, find the current Bundle’s ClassPath and use your own class loader to load it

(5) Otherwise, check whether the class is in the Fragment Bundle. If so, delegate the load to the class loader of the Fragment Bundle

(6) Otherwise, find the Bundle in the Dynamic Import list and delegate the load to the corresponding Bundle’s class loader

(7) Otherwise, class search fails