Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

The Art of JVM: how to use Class Loader

2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article mainly explains "the art of JVM how to use class loader", the content of the explanation is simple and clear, easy to learn and understand, now please follow the editor's ideas slowly in depth, together to study and learn "the art of JVM how to use class loader" bar!

Before formally introducing the thread's context class loader, you need to introduce something theoretical.

Current class loader (Current ClassLoader): each class uses its own class loader (both loading its own class loader) to load other classes (referring to dependent classes). If ClassX references ClassY, then ClassX's class loader will load ClassY (provided that ClassY has not been loaded).

Thread context class loader (ContextClassLoader): thread context class loader is introduced from JDK1.2. * * getContextClassLoader () and setContextClassLoader (ClassLoader cl) * * in class Thread are used to get and set context class loaders, respectively. If it is not set through setContextClassLoader (ClassLoader cl), the thread inherits the context class loader of its parent thread.

The context class loader for the initial thread of the Java application runtime is the system class loader

Why use thread context class loading?

Why use thread context class loading? I also briefly mentioned it in the last article. The original design intention of thread context class loading lies in the SPI mechanism of our JAVA language. I also provide a diagram, hoping that the following diagram can fully explain the meaning of the context class loader.

The importance of thread context class loader

When we use JDBC to manipulate the database, we will write as follows:

Class.forName ("com.mysql.driver.Driver")

Connection conn = Driver.getConnection ()

Statement st = conn.getStatement ()

JDBC is a standard, which means that both Connection and Statement are standards built into JDK, are abstract interfaces, and are located in rt.jar, and their implementation must be implemented by different database vendors, then the problem arises: these standards are loaded by the root class loader, but the specific implementation is done by the specific vendor. It must be necessary to put the manufacturer's jar into the classpath of the project to use. Obviously, these classes of the manufacturer cannot be loaded by the startup class loader, but will be loaded by the application class loader. According to * *, the classes or interfaces loaded by the parent class loader cannot see the classes or interfaces loaded by the child class loader. The principle that the class or interface loaded by the subclass loader is able to see the class or interface loaded by the parent class loader will lead to such a situation: for example, a class under the java.sql package will be loaded by the startup class loader, and the class may have to access the specific implementation class, but the specific implementation class is loaded by the application class loader. The java.sql class loader is based on the lack of concrete implementation of the classes loaded by the classloader, which is a very fatal problem based on the parent delegate model, which occurs not only in JDBC, but also in SPI (Service Provider Interface) * * scenarios such as JNDI and xml parsing, so here's a summary: the parent ClassLoader can load classes using the ClassLoader specified by the current thread Thread.currentThread (). GetContextLoader () This changes the situation where the parent ClassLoader cannot use the child ClassLoader or other classes loaded by the ClassLoader that does not have a direct parent-child relationship, and changes the parent delegate model. The thread context class loader is the Current ClassLoader of the current thread. Under the parent delegate model, class loading is bottom-up, and the lower-level class loader will delegate the upper layer to load. But for SPI, some interfaces are provided by the Java core library, while the Java core library is loaded by the startup class loader, and the implementation of these interfaces comes from different jar packages (provided by the vendor). Java's startup class loader does not load jar packages from other sources, so the traditional parent delegate model can not meet the requirements of SPI. By setting the context class loader for the current thread, the interface implementation class can be loaded by the set context class loader.

The following is a more specific description of the SPI scenario of JDBC with a diagram:

Obviously, JDBC will refer to the implementation of the specific vendor of JDBCImpl, and the JDBC standard is loaded by the root class loader, so the classes of the specific implementation vendor will also use the root class loader to load, and because they are in the project of classPath, by the system class loader to load, it is obvious that there is no way to be loaded by the root class loader, in order to solve this problem, the thread's context class loader plays a role.

Analysis: from the above theory, we can know that the context class loader of the initial thread of the Java application runtime is the system class loader.

Then think about it: why is the default thread context classloader the system classloader? It must have been set somewhere, but it is actually set in Launcher, as follows: 1. The general usage mode of thread context class loader (get-use-restore) ClassLoader classLoader = Thread.currentThread (). GetContextClassLoader (); / / get

Try {

ClassLoader targetTccl = context class logger to be set by xxx;//

Thread.currentThread () .setContextClassLoader (targetTccl); / / set

MyMethod (); / / use

} finally {

Thread.currentThread () .setContextClassLoader (classLoader); / / restore

2. If a class is loaded by class loader A, then the dependent class of that class is also loaded by the same class loader (if the dependent class has not been loaded before). The purpose of ContextClassLoader is to break the class loading delegate mechanism of Java. 3. When the high level provides a unified interface for the lower level to implement, and at the same time loads (or instantiates) the low-level class at the high level, it is necessary to use the thread context class loader to help the high-level ClassLoader find and load the class. Thread.currentThread () .getContextClassLoader (); / / get

Thread.currentThread () .setContextClassLoader (targetTccl); / / set

At this point, the thread context class loader ends here.

The process of class loading

In fact, a class from loading to use is to go through a lot of processes, let's talk in detail about the process of a class from loading to initialization, but there are still some unknown.

Here is a picture:

Fixed class load execution order: the order in which the load verification is ready to initialize the unload is certain. Why is the parsing process not in this order? (next analysis)

It is not certain when class loading is triggered, but class initialization is required in the following four cases. However, the three stages of load verification preparation must be performed before initialization.

Trigger the process of class loading (class loading caused by the initialization process)

1): use the new keyword to get a static property to set a static property to call a static method.

Int myValue = SuperClass.value; causes the parent class to initialize, but not the subclass initialization

SuperClass.Value = 3; causes parent class initialization, not subclass initialization.

SubClass.staticMethod (); initialize the parent class before initializing the subclass

SubClass sc = new SubClass (); initialize the parent class first and then initialize the subclass

2): when using reflection, if it is found that the class has not been initialized, it will be initialized.

Class clazz = Class.forName ("com.hnnd.classloader.SubClass")

3): when initializing a class, if it is found that the parent class is not initialized, the parent class will be initialized first

SubClass.staticMethod (); initialize the parent class before initializing the subclass

4): when starting the virtual machine, you need to load the class that contains the main method.

Class SuperClass {

Public static int value = 5

Static {

System.out.println ("Superclass. Init.")

}

}

Class SubClass extends SuperClass {

Static {

System.out.println ("subClass*init")

}

Public static void staticMethod () {

System.out.println ("superclass value" + SubClass.value)

}

}

Let's explain the process of loading, connecting, and initializing the class one by one:

1: load

Get the byte stream of the corresponding class according to the full class name (source class file, network file, and reflected Proxygeneraotor.generaotorProxyClass of the byte stream)

1.2) load the static data structure in the byte stream into the runtime data structure in the method area

1. 3) generate a java.lang.Class object in memory that can be used to manipulate the data structure in the method area (through reflection)

2: verify

Verification of file format: verify the beginning of the 0XCAFFBASE at the beginning of the class file

Verify that the major and minor version numbers are within the scope of the current virtual machine, etc.

Detect constant types that are not supported by jvm

Validation of metadata:

Verify whether this class has a parent class

Verify that classes modified by classes that do not allow inheritance (final) are inherited

Verify that all interfaces and parent class interfaces are implemented when this class is not an abstract class

* * bytecode verification: * * verify that the jump instruction jumps to an instruction other than the method.

Verify that the type conversion is valid, for example, it is OK to assign a reference to the parent class to the subclass object, but it is dangerous to assign the parent class object to the subclass reference

In short: bytecode verification passed, does not mean that the bytecode must be no problem, but bytecode verification does not pass. Then there must be something wrong with the bytecode file.

Verification of symbol references (which occurs during parsing):

Whether the corresponding class can be found by the full class name described by the string.

Specifies whether the class contains field descriptors, as well as simple field and method names.

3: prepare: allocate memory for class variables and set initial values.

For example, public static int value = 123

Value=0 instead of 123in the process of preparation, when the initialization method of the class is executed, value=123

If it is a static constant

Public static final int value = 9; so in the process of preparation, value is 9.

4: parsing: replace symbolic references with direct references

Symbol reference classification:

Symbolic references to CONSTANT_Class_info classes or interfaces

Symbolic reference to the CONSTANT_Fieldref_info field

Symbolic reference to the CONSTANT_Methodref_info method

Symbolic references to methods in the CONSTANT_intfaceMethodref_info- interface

A symbolic reference to a CONSTANT_NameAndType_info subclass or method.

CONSTANT_MethodHandle_Info method handle

CONSTANT_InvokeDynamic_Info dynamic invocation

Direct reference:

A pointer to an object

Relative offset

Operation handle

5: initialization: the last step of class loading when a class is initialized: execute the class constructor to assign values to all class variables (the compiler generates CLInit)

What is a class constructor? The class constructor is determined by the compiler according to the order in which the total class variables and static code blocks appear in the Java source file

Static statements can only access class variables defined before static statements, and static variables that follow can be assigned but cannot be accessed.

The static code block in the parent class takes precedence over the subclass static code block.

If there are no static code blocks and no static class variables in the class, the compiler will not generate methods for the Clint class constructor.

Public class TestClassInit {

Public static void main (String [] args) {

System.out.println (SubClass.sub_before_v)

}

}

Class SubClass extends SuperClass {

Public static int sub_before_v = 5

Static {

Sub_before_v = 10

System.out.println ("subclass init.")

Sub_after_v=0

/ / error, the code in the static code block can only be assigned to the following class variables but cannot be accessed.

Sub_before_v = sub_after_v

}

Public static int sub_after_v = 10

}

Class SuperClass {

Public static int super_before_v = 5

Static {

System.out.println ("superclass init.")

}

Public static int super_after_v = 10

}

Let's verify the above through a series of cases. Let's make a little summary first.

Initialization of a class requires active use of the class. The following summarizes several points, all of which can be regarded as active use of the class:

1: create an instance of the class.

2: access or assign values to static variables in a class or interface.

3: access the static method of a class.

4: reflection.

5: initialize a subclass of a class.

6: the class that contains the main method.

7:jdk1.7 began to provide support for dynamic languages.

Except for the above 7 cases, all of them are used passively and will not cause the class to be initialized.

According to the above conclusion, let's write a few cases and prove it for each case.

Conclusion 1:

The static constant initialization process is that after the jvm connection, the initialization of the static constant is saved in the constant pool of the class where the static constant method is called. At this time, the class file of the class where the static constant is called can be deleted, and even if deleted, the constant is still valid. Calling a static constant of a class cannot initialize the class.

Code:

Package com.jdyun.jvm001

Public class TestClass03 {

Public static void main (String [] args) {

System.out.println (Pet1.a)

}

}

Class Pet1 {

Public static final int a = 10

Static {

System.out.println ("I'm Pet1, I'm initialized")

}

}

Running result:

"C:\ Program Files\ Java\ jdk-11.0.2\ bin\ java.exe"-javaagent:C:\ Program Files\ JetBrains\ IntelliJ IDEA 2019.2\ lib\ idea_rt.jar=64451:C:\ Program Files\ JetBrains\ IntelliJ IDEA 2019.2\ bin "- Dfile.encoding=UTF-8-classpath G:\ jdyun-jvm\ out\ production\ jdyun-jvm com.jdyun.jvm001.TestClass03

ten

Process finished with exit code 0

As we can see from the above case, one class calling the constant of another class does not result in the initialization of a class.

Conclusion 2: the static constant declared here, according to the previous understanding, is that when the static constant is called, it does not initialize the class in which the static constant is located, but here when the value of the static constant is a reference type, the class in which the static constant is located will be initialized, so I will be initialized first, and then print the random value of a.

Code:

Package com.jdyun.jvm001

Import java.util.UUID

Public class TestClass03 {

Public static void main (String [] args) {

System.out.println (Pet1.a)

}

}

Class Pet1 {

Public static final String a = UUID.randomUUID () .toString ()

Static {

System.out.println ("I am initialized")

}

}

Running result:

"C:\ Program Files\ Java\ jdk-11.0.2\ bin\ java.exe"-javaagent:C:\ Program Files\ JetBrains\ IntelliJ IDEA 2019.2\ lib\ idea_rt.jar=50237:C:\ Program Files\ JetBrains\ IntelliJ IDEA 2019.2\ bin "- Dfile.encoding=UTF-8-classpath G:\ jdyun-jvm\ out\ production\ jdyun-jvm com.jdyun.jvm001.TestClass03

I'm initialized.

E5b56749-5a97-405f-9fe9-dfe4211bc0ce

Process finished with exit code 0 conclusion 3:

Static variable initialization is different from static constant initialization. Static variable initialization is assigned a real value such as int a = 2 during the initialization phase, then 2 is actually assigned to a.

If a class invokes a static variable of that class, the class in which the static variable resides is considered to be called actively. Then the class is initialized.

If there is a static code block in this class, then the static code block takes precedence over static variables.

If there is a parent class in which the static variable resides, the parent class is initialized first.

Package com.jdyun.jvm001

Import java.util.Random

Import java.util.UUID

Public class TestClass03 {

Public static void main (String [] args) {

System.out.println (Dog3.a)

}

}

Class Dog3 extends Pet1 {

Public static final int a = new Random () .nextInt ()

Static {

System.out.println ("I'm Pet1, I'm the parent, and I'm the first to load")

}

}

Class Pet1 {

Static {

System.out.println ("I am initialized")

}

}

Running result:

"C:\ Program Files\ Java\ jdk-11.0.2\ bin\ java.exe"-javaagent:C:\ Program Files\ JetBrains\ IntelliJ IDEA 2019.2\ lib\ idea_rt.jar=64951:C:\ Program Files\ JetBrains\ IntelliJ IDEA 2019.2\ bin "- Dfile.encoding=UTF-8-classpath G:\ jdyun-jvm\ out\ production\ jdyun-jvm com.jdyun.jvm001.TestClass03

I'm initialized.

I'm Pet1, I'm the parent, and I was loaded first.

-1203457101

Process finished with exit code 0 conclusion 4:

Verify the number of initialization times and will only be initialized once.

Package com.jdyun.jvm001

Import java.util.Arrays

Import java.util.List

Import java.util.Random

Import java.util.UUID

Public class MyTest02 extends ClassLoader {

Public static void main (String [] args) throws ClassNotFoundException {

/ / 1, verify initialization times

For (int iTuno Bandi)

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report