JVM performance optimization – class loader, manually implement hot loading of classes

1. Hierarchy of class loading mechanism

Each written class file with the “.java” extension stores the program logic that needs to be executed. These “.java” files are compiled into files with the extension “.class” by the Java compiler. The “.class” files are saved in According to the virtual machine instructions after the Java code is converted, when a certain class needs to be used, the virtual machine will load its “.class” file, create the corresponding class object, and load the class file into the memory of the virtual machine. This The process is called class loading. Here we need to understand the process of class loading, as follows:

Jvm executes class files

Step 1. Class loading mechanism

Load the bytecode content of the class file into memory, convert these static data into runtime data structures in the method area, and generate a java.lang.Class object representing this class in the heap as the method area class data To access the entrance, this process requires the participation of the class loader.

When the system is running, the class loader transfers the binary data of the .class file from external memory (such as CD, hard disk) into the memory. The CPU then reads the instructions and data from the memory to perform operations, and stores the operation results in the memory. . Memory plays the role of “setter” in this process. In layman’s terms, if there is no memory, the class loader transfers the .class file binary data from the external storage device directly to the CPU for processing. Due to the processing speed of the CPU It is much faster than the speed of transferring data, which can easily lead to data disconnection, so memory is needed to buffer.

After the class loads the .class file into the method area at runtime, a Java.lang.Class object will be created in the heap to encapsulate the data structure of the class in the method area. The Class object is created during the process of loading the class. Yes, each class corresponds to an object of Class type. The constructor of the Class class is private and can only be created by the JVM. Therefore, the Class object is the entrance to reflection. Using this object, you can obtain the specific data structure in the .class file associated with the target class.

The final product of class loading is the Class object (note that it is not the target class object) located in the heap. This object encapsulates the data structure of the class in the method area and provides the user with an interface to access the data structure of the method area, which is Java reflection. interface.

Now I have also found a lot of test friends and created a communication group to share technology, sharing a lot of technical documents and video tutorials we collected.
If you don’t want to experience the feeling of not being able to find resources when studying on your own, having no one to answer your questions, and persisting for a few days before giving up.
You can join us to communicate. And there are many technical experts who have made certain achievements in automation, performance, security, test development, etc.
Share their experience, and also share many live lectures and technical salons
You can learn for free! Focus on it! Open source! ! !
QQ group number: 110685036

Step 2, connection process

The process of merging the binary code of java classes into the running state of the JVM

Verification: Ensure that the loaded class information complies with JVM specifications and has no security issues

Preparation: The stage of formally allocating memory for class variables (static variables) and setting the initial value of the class variable. These memories will be allocated in the method area.

Analysis: The symbolic reference of the virtual machine constant pool is replaced by the byte reference process

Step 3. Initialization

The initialization phase is the process of executing the class constructor () method. The class constructor () method is generated by the compiler automatically collecting the assignment actions of all class variables in the class and merging the statements in the static statement block (static block). The code is from top to bottom. implement.

When initializing a class, if it is found that its parent class has not been initialized, you need to trigger the initialization of its parent class first.

The virtual machine ensures that the () method of a class is correctly locked and synchronized in a multi-threaded environment.
When scoped a static field of a Java class, only the class that actually declares the field will be initialized.

2. Hierarchy of class loaders

Bootstrap class loader

Extension class loader

System (-) class loader

1. Start (Bootstrap) class loader

The startup class loader mainly loads the classes needed by the JVM itself. This class loading is implemented in the C++ language and is part of the virtual machine itself. It is responsible for loading the classes under the /lib path. The core class library or the jar package under the path specified by the -Xbootclasspath parameter is loaded into the memory. Note that the virtual machine loads the jar package according to the file name recognition, such as rt.jar. If the file name is not recognized by the virtual machine, even if the file name is not recognized by the virtual machine, the jar package will be loaded into the memory. It has no effect if the jar package is thrown into the lib directory (for security reasons, the Bootstrap startup class loader only loads classes whose package names begin with java, javax, sun, etc.).

2. Extension class loader

The extended class loader refers to the sun.misc.Launcher$ExtClassLoader class implemented by Sun (acquired by Oracle). It is implemented in the Java language and is the static internal class of Launcher. It is responsible for loading /lib/ For class libraries in the ext directory or in the path specified by the system variable -Djava.ext.dir, developers can directly use the standard extension class loader.

3. System class loader

Also known as the application loader, it refers to the sun.misc.Launcher$AppClassLoader implemented by Sun. It is responsible for loading the class library under the path specified by the system class path java -classpath or -D java.class.path, which is the classpath path we often use. Developers can directly use the system class loader. Under normal circumstances, this class is loaded It is the default class loader in the program, which can be obtained through the ClassLoader#getSystemClassLoader() method.

In the daily application development of Java, the loading of classes is almost executed by the above three class loaders in cooperation with each other. When necessary, we can also customize the class loader. It should be noted that the Java virtual machine uses It is an on-demand loading method, which means that when the class needs to be used, its class file will be loaded into the memory to generate a class object, and when loading the class file of a certain class, the Java virtual machine adopts the parental delegation mode. That is, the request is handed over to the parent class for processing. It is a task delegation model. Let’s learn more about it below.

3.1. Understanding the Parental Delegation Model

Below we learn about several class loaders defined in Java and their implementation of the parent delegation mode from the code level. Their class diagram relationships are as follows

The parent delegation mode was introduced after Java 1.2. The working principle is that if a class loader receives a class loading request, it will not load it first, but delegate the request to the loader of the parent class. Execution, if the parent class loader still has its parent class loader, it will be further delegated upward, recursively, and the request will eventually reach the top-level startup class loader. If the parent class loader can complete the class loading task, it will return successfully. If If the parent class loader cannot complete this loading task, the child loader will try to load it by itself. This is the parent delegation model, that is, each son is lazy and leaves it to his father every time there is work, until the father says this When I can’t do something, my son will find a way to complete it on his own. Isn’t this the legendary trick of strength? So what is the use of adopting this model?

3.1. Advantages of Parental Delegation Model

The advantage of using the parent delegation mode is that the Java class has a priority hierarchical relationship with its class loader. Through this hierarchical relationship, repeated loading of the class can be avoided. When the father has already loaded the class, , there is no need to load the sub-ClassLoader again. Secondly, considering security factors, the types defined in the Java core API will not be replaced arbitrarily. Assume that a class named java.lang.Integer is passed through the network and passed to the startup class loader through the parent delegation mode, and the startup class loader A class with this name is found in the core Java API and it is found that the class has been loaded. The java.lang.Integer passed over the network will not be reloaded, but the loaded Integer.class will be directly returned. This can prevent the core API from being loaded. The library has been tampered with at will. You may be thinking, what if we customize a class named java.lang.SingleInterge on the classpath (this class is made up)? This class does not exist in java.lang. It is passed to the startup class loader through the parent delegation mode. Since the class does not exist in the path of the parent class loader, it will not be loaded and will be reversely delegated to the child class loader for loading. , the class will eventually be loaded through the system class loader. However, this is not allowed because java.lang is a core API package and requires access permissions. Forced loading will report the following exception.

java.lang.SecurityException: Prohibited package name: java.lang

So it cannot be loaded successfully no matter what.

3. Relationship between class loaders

We further understand the relationship between class loaders (not referring to the inheritance relationship), which can be divided into the following four points

  • Starts the class loader, implemented in C++, without a parent class.
  • Extended class loader (ExtClassLoader), implemented in Java language, the parent class loader is null
  • System class loader (AppClassLoader), implemented in Java language, and the parent class loader is ExtClassLoader
  • Custom class loader, the parent class loader must be AppClassLoader.

1. Common methods of class loaders

loadClass(String)

This method loads a binary type with a specified name (including package name). This method is no longer recommended for users to rewrite after JDK1.2 but users can call this method directly. The loadClass() method is implemented by the ClassLoader class itself. In this method The logic is the implementation of the parent delegation mode. The source code is as follows. loadClass(String name, boolean resolve) is an overloaded method. The resolve parameter represents whether to generate a class object and perform parsing related operations at the same time.

As shown in the loadClass method, when a class loading request comes, the class object is first searched for in the cache. If it exists, it is returned directly. If it does not exist, it is handed over to the parent loader to load the class. If there is no parent to load, then Leave it to the top-level startup class loader to load. If it is still not found, use the findClass() method to load it (findClass() will be introduced further later). From the loadClass implementation, we can also know that if we don’t want to redefine the rules for loading classes, and there is no complicated logic, and we just want to load the class we specify at runtime, then we can directly use this.getClass().getClassLoder.loadClass(“className “), so that you can directly call the loadClass method of ClassLoader to obtain the class object.

findClass(String)

Before JDK1.2, when loading a custom class, you would always inherit the ClassLoader class and override the loadClass method to implement a custom class loading class. However, after JDK1.2, users are no longer recommended to override loadClass(). method, but it is recommended to write the custom class loading logic in the findClass() method. From the previous analysis, it can be seen that the findClass() method is called in the loadClass() method. When the parent loader in the loadClass() method After the loading fails, its own findClass() method will be called to complete the class loading, so as to ensure that the customized class loader also complies with the parent delegation mode. It should be noted that the specific code logic of the findClass() method is not implemented in the ClassLoader class. Instead, a ClassNotFoundException exception is thrown. At the same time, it should be known that the findClass method is usually used together with the defineClass method (will be analyzed later)

defineClass(byte[] b, int off, int len)

The defineClass() method is used to parse the byte stream into a Class object that can be recognized by the JVM (the logic of this method has been implemented in ClassLoader). Through this method, the class object can not only be instantiated through the class file, but also through other methods. class object, such as receiving the bytecode of a class through the network, and then converting it into a byte stream to create the corresponding Class object. The defineClass() method is usually used together with the findClass() method. Generally, in a custom class loader When the class is loaded, it will directly override the findClass() method of ClassLoader and write the loading rules. After obtaining the bytecode of the class to be loaded, it will be converted into a stream, and then the defineClass() method will be called to generate the Class object of the class.

resolveClass(Class c)

Using this method, the Class object of the class can be created and parsed at the same time. Earlier we said that the link stage mainly verifies the bytecode, allocates memory for class variables and sets initial values while converting symbol references in the bytecode file into direct references.

4. Hot deployment

For Java applications, hot deployment is to update Java class files at runtime.

1. What is the principle of hot deployment

If you want to know the principle of hot deployment, you must understand the loading process of java classes. To transfer a java class file to an object in a virtual machine, the following process is required.

First, the java file is compiled into class bytecode through the java compiler. The class loader reads the class bytecode, and then converts the class into an instance. NewInstance can generate an object for the instance.

The ClassLoader function of the class loader is to convert the class bytecode into an instance of the class.

In Java applications, all instances are loaded by class loaders.

Generally in the system, the loading of classes is completed by the system’s own class loader, and a java class with the same fully qualified name (such as com.csiar.soc.HelloWorld) can only be loaded once and cannot be loaded. uninstall.

The question arises at this time, if we want to uninstall the java class and replace it with a newer version of the java class, what should we do?

Since in the class loader, a java class can only be loaded once and cannot be unloaded. Is it possible to directly change the class loader? The answer is yes, we can customize the class loader and override the findClass method of ClassLoader. To achieve hot deployment, you can divide it into the following three steps:

  1. Destroy the custom ClassLoader
  2. Update class file
  3. Create a new ClassLoader to load the updated class file.

2. Hot deployment and hot loading

2.1. The connection and difference between Java hot deployment and Java hot loading

The connection between Java hot deployment and hot loading

  1. Compile/deploy projects without restarting the server
  2. Java-based class loader implementation

The difference between Java hot deployment and hot loading

  1. Deployment method
  • Hot deployment redeploys the project while the server is running
  • Hot reloading reloads classes at runtime
  • Implementation principle
  • Hot deployment directly reloads the entire application
  • Hot reloading reloads classes at runtime
  • scenes to be used
  • Hot deployment is more commonly used in production environments
  • Hot reloading is more suitable for use in development environments.

3. Related codes

User class has not been modified

public class User {

public void add() {
System.out.println("addV1, not modified...");
}
}

User update class

public class User {

public void add() {
System.out.println("I modified the previous user add method!");
}
}

Custom class loader

public class MyClassLoader extends ClassLoader {

@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
\t\t\t// file name
String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
// Get the file input stream
InputStream is = this.getClass().getResourceAsStream(fileName);
// read bytes
byte[] b = new byte[is.available()];
is.read(b);
// Parse the byte stream into a Class object that jvm can recognize
return defineClass(name, b, 0, b.length);
} catch (Exception e) {
throw new ClassNotFoundException();
}

}

}

Update code

public class Hotswap {

public static void main(String[] args)
throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException,
SecurityException, IllegalArgumentException, InvocationTargetException, InterruptedException {
loadUser();
System.gc();
Thread.sleep(1000);//Waiting for resource recycling
//Class files that need to be hot deployed
File file1 = new File("F:\test\User.class");
// Previously compiled class files
File file2 = new File(
"F:\test\test\target\classes\com\itmayiedu\User.class");
boolean isDelete = file2.delete();//Delete the old version of the class file
if (!isDelete) {
System.out.println("Hot deployment failed.");
return;
}
file1.renameTo(file2);
System.out.println("update success!");
loadUser();
}

public static void loadUser() throws ClassNotFoundException, InstantiationException, IllegalAccessException,
NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
MyClassLoader myLoader = new MyClassLoader();
Class<?> class1 = myLoader.findClass("com.test.User");
Object obj1 = class1.newInstance();
Method method = class1.getMethod("add");
method.invoke(obj1);
System.out.println(obj1.getClass());
System.out.println(obj1.getClass().getClassLoader());
}
}

Like and follow to avoid getting lost~~~