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

How to understand Java ClassLoader

2025-03-26 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

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

The content of this article mainly focuses on how to understand Java ClassLoader. The content of the article is clear and well-organized. It is very suitable for beginners to learn and is worth reading. Interested friends can follow the editor to read together. I hope you can get something through this article!

ClassLoader is one of the most mysterious technologies in Java, and countless people are so racked by it that they don't know where the way is. Online articles are also one after another, after my personal identification, most of the content is misleading others. In this article, I will take the reader to thoroughly understand ClassLoader, and you don't have to read any other related articles in the future.

What does ClassLoader do?

As the name implies, it is used to load Class. It is responsible for converting the bytecode form of Class into an in-memory Class object. The bytecode can come from the disk file * .class in the jar package, or from the byte stream provided by the remote server. The bytecode is essentially a byte array [] byte, which has a specific complex internal format.

There are many bytecode encryption techniques that rely on custom ClassLoader. First use the tool to encrypt the bytecode file, and then load the decrypted bytecode after the runtime uses a custom ClassLoader to decrypt the contents of the file.

There is a classLoader field inside each Class object to identify which ClassLoader it was loaded by.

Class Class {

...

Private final ClassLoader classLoader

...

}

Delayed loading

JVM does not load all the classes needed at once; it is loaded on demand, that is, delayed loading. In the process of running, the program will gradually encounter a lot of new classes that you don't know, and then ClassLoader will be called to load these classes. After loading, the Class object will be stored in ClassLoader, and there will be no need to reload it next time.

For example, when you call a static method of a class, the class must first need to be loaded, but it will not touch the instance field of the class, then the category Class of the instance field can not be loaded for the time being, but it may load the category related to the static field, because the static method will access the static field. The category of the instance field may not be loaded until you instantiate the object.

attend to each one's own duties

There will be multiple ClassLoader in the running instance of JVM, and different ClassLoader will load bytecode files from different places. It can be loaded from different file directories, from different jar files, or from different service addresses on the network.

Three important ClassLoader are built into JVM, namely BootstrapClassLoader, ExtensionClassLoader, and AppClassLoader.

BootstrapClassLoader is responsible for loading the JVM runtime core classes, which are located in the JAVA_HOME/lib/rt.jar file, where our commonly used built-in libraries java.xxx.*, such as java.util.*, java.io.*, java.nio.*, java.lang.*, and so on. This ClassLoader is special, it is implemented by C code, and we call it the "root loader".

ExtensionClassLoader is responsible for loading JVM extension classes, such as the swing series, the built-in js engine, the xml parser, and so on. These libraries usually start with javax, and their jar packages are located in JAVA_HOME/lib/ext/*.jar, and there are many jar packages.

AppClassLoader is the direct loader for our users, loading the jar packages and directories in the path defined in the Classpath environment variable. It usually loads the code we write and the third-party jar packages we use.

For jar packages and class files provided by static file servers on the network, jdk has a built-in URLClassLoader so that users only need to pass the standard network path to the constructor and use URLClassLoader to load remote class libraries. URLClassLoader can load not only remote class libraries, but also local path class libraries, depending on the address form in the constructor. ExtensionClassLoader and AppClassLoader are subclasses of URLClassLoader, and they both load class libraries from the local file system.

AppClassLoader can be obtained from the static method getSystemClassLoader () provided by the ClassLoader class, which is what we call the "system class loader", which usually loads the class code we users write. When our main method executes, the first user class loader is AppClassLoader.

ClassLoader transitivity

While the program is running, it encounters an unknown class. Which ClassLoader will it choose to load it? The strategy of the virtual machine is to use the ClassLoader of the caller's Class object to load the currently unknown class. What is the caller Class object? The virtual machine must be running a method call (static method or instance method) when it encounters this unknown class. The class on which the method is hung is the caller Class object. We mentioned earlier that there is a classLoader property in each Class object that records who loads the current class.

Because of the transitivity of ClassLoader, all classes that load late are taken care of by the ClassLoader that initially called the main method, which is AppClassLoader.

Parental appointment

We mentioned earlier that AppClassLoader is only responsible for loading the class libraries under the Classpath. If you encounter a system class library that is not loaded, AppClassLoader must leave the loading of the system class library to BootstrapClassLoader and ExtensionClassLoader. This is what we often call "parent delegation".

When AppClassLoader loads an unknown class name, it does not immediately search for Classpath, it will first give the class name to ExtensionClassLoader to load, if ExtensionClassLoader can load, then AppClassLoader does not have to bother. Otherwise it will search for Classpath.

When ExtensionClassLoader loads an unknown class name, it does not immediately search for the ext path, it will first give the class name to BootstrapClassLoader to load, if BootstrapClassLoader can load, then ExtensionClassLoader will not bother. Otherwise, it will search for jar packages under the ext path.

A cascading father-son relationship has been formed between the three ClassLoader. Each ClassLoader is very lazy. Try to leave the work to the father, and the father will do it only if he can't do it. Inside each ClassLoader object, there is a parent property that points to its parent loader.

Class ClassLoader {

...

Private final ClassLoader parent

...

}

It is worth noting that the parent pointer of the ExtensionClassLoader in the figure is dotted because its parent value is null, and when the parent field is null, its parent loader is the "root loader". If the value of the classLoader property of a Class object is null, then the class is also loaded by the root loader.

Class.forName

When we use the jdbc driver, we often use the Class.forName method to dynamically load the driver class.

Class.forName ("com.mysql.cj.jdbc.Driver")

The principle is that there is a static code block in the mysql-driven Driver class, which is executed when the Driver class is loaded. This static code block registers the mysql driver instance with the global jdbc driver manager.

Class Driver {

Static {

Try {

Java.sql.DriverManager.registerDriver (new Driver ())

} catch (SQLException E) {

Throw new RuntimeException ("Can't register driver!")

}

}

...

}

The forName method also uses the ClassLoader of the caller's Class object to load the target class. However, forName also provides a multi-parameter version, so you can specify which ClassLoader to use to load

Class forName (String name, boolean initialize, ClassLoader cl)

Through this form of forName method, we can break through the limitation of built-in loader and allow us to load class libraries from any other source freely by using custom class loader. Depending on the transitivity of ClassLoader, other class libraries referenced by the target class library will also be loaded using a custom loader.

Custom loader

There are three important methods in ClassLoader: loadClass (), findClass (), and defineClass ().

The loadClass () method is the entry to load the target class. It first looks up the current ClassLoader and whether the target class has been loaded in its parents. If it does not find it, it will let the parents try to load it. If neither parent can load it, it will call findClass () to let the custom loader load the target class itself. The findClass () method of ClassLoader needs to be overridden by a subclass, and different loaders will use different logic to get the bytecode of the target class. After getting the bytecode, call the defineClass () method to convert the bytecode into a Class object. Now I'm going to use pseudo code to show the basic process.

Class ClassLoader {

/ / loading entry, defining parent delegation rules

Class loadClass (String name) {

/ / whether it has been loaded

Class t = this.findFromLoaded (name)

If (t = = null) {

/ / give it to your parents

T = this.parent.loadClass (name)

}

If (t = = null) {

/ / both parents are not good, so they are on their own.

T = this.findClass (name)

}

Return t

}

/ / leave it to the subclass to implement it.

Class findClass (String name) {

Throw ClassNotFoundException ()

}

/ / assemble Class objects

Class defineClass (byte [] code, String name) {

Return buildClassFromCode (code, name)

}

}

Class CustomClassLoader extends ClassLoader {

Class findClass (String name) {

/ / find bytecode

Byte [] code = findCodeFromSomewhere (name)

/ / assemble Class objects

Return this.defineClass (code, name)

}

}

Custom class loaders are not easy to break parent delegation rules, and do not easily override loadClass methods. Otherwise, the custom loader may not be able to load the built-in core class library. When using a custom loader, make it clear who its parent loader is and pass in the parent loader through the constructor of the subclass. If the parent loader is null, it means that the parent loader is the root loader.

/ / ClassLoader constructor

Protected ClassLoader (String name, ClassLoader parent)

The parent delegation rule may become a three-parent delegate and a four-parent delegate, depending on which parent loader you use, it will recursively delegate all the way to the root loader.

Class.forName vs ClassLoader.loadClass

Both of these methods can be used to load the target class, and one small difference between them is that the Class.forName () method gets the Class of the native type, while ClassLoader.loadClass () reports an error.

Class x = Class.forName ("[I")

System.out.println (x)

X = ClassLoader.getSystemClassLoader () .loadClass ("[I")

System.out.println (x)

-

Class [I

Exception in thread "main" java.lang.ClassNotFoundException: [I

...

Diamond dependence

There is a well-known concept in project management called "diamond dependency", which means that software dependency causes two versions of the same software package to coexist rather than conflict.

We usually use maven to solve diamond dependence in this way, it will choose one of multiple conflicting versions to use, if the compatibility between different versions is very poor, then the program will not compile and run properly. This form of Maven is called "flattening" dependency management.

Using ClassLoader can solve the problem of diamond dependence. Different versions of software packages are loaded with different ClassLoader, and classes with the same name in different ClassLoader are actually different classes. Let's try a simple example using URLClassLoader, whose default parent loader is AppClassLoader

$cat ~ / source/jcl/v1/Dep.java

Public class Dep {

Public void print () {

System.out.println ("v1")

}

}

$cat ~ / source/jcl/v2/Dep.java

Public class Dep {

Public void print () {

System.out.println ("v1")

}

}

$cat ~ / source/jcl/Test.java

Public class Test {

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

String v1dir = "file:///Users/qianwp/source/jcl/v1/";

String v2dir = "file:///Users/qianwp/source/jcl/v2/";

URLClassLoader v1 = new URLClassLoader (new URL [] {new URL (v1dir)})

URLClassLoader v2 = new URLClassLoader (new URL [] {new URL (v2dir)})

Class depv1Class = v1.loadClass ("Dep")

Object depv1 = depv1Class.getConstructor () .newInstance ()

Depv1Class.getMethod ("print") .invoke (depv1)

Class depv2Class = v2.loadClass ("Dep")

Object depv2 = depv2Class.getConstructor () .newInstance ()

Depv2Class.getMethod ("print") .invoke (depv2)

System.out.println (depv1Class.equals (depv2Class))

}

}

Before running, we need to compile the dependent class library

$cd ~ / source/jcl/v1

$javac Dep.java

$cd ~ / source/jcl/v2

$javac Dep.java

$cd ~ / source/jcl

$javac Test.java

$java Test

V1

V2

False

In this example, if two URLClassLoader points to the same path, the following expression is still false, because even a class loaded with the same bytecode with different ClassLoader is not the same class.

Depv1Class.equals (depv2Class)

We can also have two different versions of the Dep class implement the same interface, which avoids using reflection to call methods in the Dep class.

Class depv1Class = v1.loadClass ("Dep")

IPrint depv1 = (IPrint) depv1Class.getConstructor () .newInstance ()

Depv1.print ()

It is true that ClassLoader can solve the problem of dependency conflicts, but it also limits the interface of different software packages to be called dynamically by reflection or interface. Maven does not have this limitation, it depends on the default lazy loading strategy of the virtual machine, if you do not show the use of custom ClassLoader, then you are using AppClassLoader from beginning to end, and different versions of classes of the same name must be loaded with different ClassLoader, so Maven can not solve the diamond dependency perfectly.

If you want to know if there is an open source package management tool that can solve diamond dependency, I recommend you to learn about sofa-ark, which is Ant Financial Services Group's open source lightweight class isolation framework.

Division of labor and cooperation

Here we re-understand the meaning of ClassLoader, which is equivalent to the namespace of a class and acts as class isolation. Class names in the same ClassLoader are unique, and different ClassLoader can hold classes with the same name. ClassLoader is the container of the class name and the sandbox of the class.

There will also be cooperation between different ClassLoader, and the cooperation between them is done through the parent attribute and parent delegation mechanism. Parent has a higher load priority. In addition, parent also expresses a sharing relationship. When multiple sub-ClassLoader share the same parent, then the classes contained in this parent can be considered shared by all sub-ClassLoader. This is why BootstrapClassLoader is treated as an ancestor loader by all classloaders, and the JVM core class library should naturally be shared.

Thread.contextClassLoader

If you have read the source code of Thread a little bit, you will find a very special field in its instance field.

Class Thread {

...

Private ClassLoader contextClassLoader

Public ClassLoader getContextClassLoader () {

Return contextClassLoader

}

Public void setContextClassLoader (ClassLoader cl) {

This.contextClassLoader = cl

}

...

}

ContextClassLoader "thread context class loader", what on earth is this?

First of all, contextClassLoader is the kind of classloader that needs to be displayed, and if you don't show it, you'll never use it anywhere. You can use the following ways to display and use it

Thread.currentThread (). GetContextClassLoader (). LoadClass (name)

This means that if you use the forName (string name) method to load the target class, it will not automatically use contextClassLoader. Classes that are lazily loaded because of dependencies on the code are not automatically loaded using contextClassLoader.

The contextClassLoader of the second thread is inherited from the parent thread, which is the thread that created the current thread. The contextClassLoader of the main thread when the program starts is AppClassLoader. This means that if there is no manual setting, then the contextClassLoader of all threads is AppClassLoader.

So what on earth is this contextClassLoader for? We will use the previously mentioned principle of division and cooperation of classloaders to explain its purpose.

It can share classes across threads, as long as they share the same contextClassLoader. ContextClassLoader is automatically passed between parent and child threads, so sharing will be automated.

If different threads use different contextClassLoader, the classes used by different threads can be isolated.

If we divide the business, different businesses use different thread pools, the same contextClassLoader is shared within the thread pools, and different contextClassLoader is used between the thread pools, which can play a good role of isolation and protection and avoid class version conflicts.

If we do not customize contextClassLoader, then all threads will use AppClassLoader by default and all classes will be shared.

The use of contextClassLoader for threads is rare, so don't worry too much if the above logic is obscure.

After adding module function, JDK9 modifies the structure design of class loader to some extent, but the principle of class loader is similar. As the container of class, it plays the role of class isolation. At the same time, it also needs to rely on the parent delegation mechanism to establish the cooperative relationship between different class loaders.

Thank you for your reading. I believe you have a certain understanding of "how to understand Java ClassLoader". Go and practice it. If you want to know more about it, you can follow the website! The editor will continue to bring you better articles!

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