JVM performance optimization - class loader, manually realize the 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 entry, 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, and because the processing speed of the CPU is much faster Due to the speed of transferring data, it is easy to cause 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 the Class type. The constructor of the Class class is private and only the JVM can create it. Therefore, the Class object is the entry point of reflection, and the specific data structure in the .class file associated with the target class can be obtained by using this object.


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.

现在我也找了很多测试的朋友,做了一个分享技术的交流群,共享了很多我们收集的技术文档和视频教程。
如果你不想再体验自学时找不到资源,没人解答问题,坚持几天便放弃的感受
可以加入我们一起交流。而且还有很多在自动化,性能,安全,测试开发等等方面有一定建树的技术大牛
分享他们的经验,还会分享很多直播讲座和技术沙龙
可以免费学习!划重点!开源的!!!
qq群号:110685036

Step 2. Connection process

The process of merging the binary code of a java class 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 <clinit>()method. The class constructor <clinit>()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), and the code is executed from top to bottom.

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 a class's <clinit>()methods are correctly locked and synchronized in a multi-threaded environment.
When scoped to a static field of a Java class, only the class that actually declares this field will be initialized.

2. Hierarchy of class loaders

Bootstrap class loader

Extension class loader

System (-) class loader

1. Start the (Bootstrap) class loader

The startup class loader mainly loads the classes needed by the JVM itself. This class loading is implemented in C++ language and is part of the virtual machine itself. It is responsible for loading the core class library under the path <JAVA_HOME>/libor the jar package under the path specified by the -Xbootclasspath parameter. Load into the memory, please note that the virtual machine recognizes and loads the jar package according to the file name, such as rt.jar. If the file name is not recognized by the virtual machine, even if the jar package is thrown into the lib directory, it will have no effect (for reasons 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 the directory or by the system variable-Djava <JAVA_HOME>/lib/ext. ext.dir specifies the class library in the bit path, and developers can directly use the standard extension class loader.

3. System class loader

Also called application loader refers to 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. Understand the parental delegation model

Let's understand the implementation of several class loaders defined in Java and their parent delegation mode from the code level. Their class diagram relationship is 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 the inheritance relationship), which can be divided into the following four points

  • Start the class loader, implemented in C++, without a parent class.
  • Extended class loader (ExtClassLoader), implemented by the Java language, the parent class loader is null
  • The system class loader (AppClassLoader), implemented by the Java language, 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, the java class can only be loaded once and cannot be unloaded. Is it possible to change the class loader directly? The answer is yes, we can customize the class loader and override the findClass method of ClassLoader. To achieve hot deployment, you can divide 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,没有修改过...");
	}
}

User update class

public class User {

	public void add() {
		System.out.println("我把之前的user add方法修改啦!");
	}
}

Custom class loader

public class MyClassLoader extends ClassLoader {

	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		try {
			// 文件名称
			String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
			// 获取文件输入流
			InputStream is = this.getClass().getResourceAsStream(fileName);
			// 读取字节
			byte[] b = new byte[is.available()];
			is.read(b);
			// 将byte字节流解析成jvm能够识别的Class对象
			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);// 等待资源回收
		// 需要被热部署的class文件
		File file1 = new File("F:\\test\\User.class");
		// 之前编译好的class文件
		File file2 = new File(
				"F:\\test\\test\\target\\classes\\com\\itmayiedu\\User.class");
		boolean isDelete = file2.delete();// 删除旧版本的class文件
		if (!isDelete) {
			System.out.println("热部署失败.");
			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());
	}
}

Finally, I would like to thank everyone who read my article carefully. Looking at the increase in fans and attention, there is always some courtesy. Although it is not a very valuable thing, if you can use it, you can take it directly!

Software testing interview applet

A software test question bank that has been used by millions of people! ! ! Who is who knows! ! ! The most comprehensive interview test mini program on the Internet, you can use your mobile phone to answer questions, take the subway, bus, and roll it up!

Covers the following interview question sections:

1. Basic theory of software testing, 2. web, app, interface function testing, 3. network, 4. database, 5. linux

6. Web, app, interface automation, 7. Performance testing, 8. Programming basics, 9. HR interview questions, 10. Open test questions, 11. Security testing, 12. Computer basics

Information acquisition method:

Guess you like

Origin blog.csdn.net/myh919/article/details/132713968