The art of JVM class loader (2)

Time:2021-1-15

Sharing is the transmission of value. Like it

introduction

Today we continue to analyze the content of class loader in depth. Last class we talked about the basic content of class loader. Please pay more attention to those who haven’t seen it. Today we continue.

What are defining class loaders and initializing class loaders?

  • Define class loader: suppose one of our classes is loaded by extclassloader, then extclassloader is called the definition class loader of the class

  • Initialization loader: the class that can return the class object reference is called the initial class loader of the class. For example, class A is loaded by our extclassloader

    Extclassloader is the definition class loader of this class, and it is also the initial class loader of this class. Our appclassloader can also return our class a reference

    Then appclassloader is also the initial class loader of the class.

What is the parental delegation model for class loaders?

In the last article, we mentioned the parental delegation model of class loader, which can also be called the parental delegation model. In today’s article, let’s make this concept clear.

Concept: a simple way to describe the concept of parental delegation. It can be understood in two parts

1. Entrustment:

When the JVM loads classes, it loads them through parental delegation and delegates them from the bottom up.

When the user-defined class loader needs to load classes, it first delegates the application class loader to load, then the application class loader delegates to the extension class loader, and the extension class loader delegates to the startup class loader.

If you start the class loader, you cannot load the class. Then load down

2 loading:

When the JVM loads a class, it loads the delegate through parental delegation, but it loads from top to bottom. When the delegate starts the class loader at the top level, it can’t delegate from top to bottom

The startup class loader starts to try to load the class. If the startup class loader fails to load, it will be handed down to the extension class loader to load. If the extension class loader fails to load, it will continue to delegate to the application class loader

To load, and so on.

If you don’t know what the parental delegation mechanism is, I’ve drawn a picture to better understand the class loading process. As follows:

From the figure above, we can know more clearly the working mechanism of the parental delegation model. In a simple word, when a class needs to be loaded, it is required to delegate upward and load downward.

Note: in the parent delegation mechanism, each loader forms a tree structure according to the parent-child relationship. Except for the root loader, each loader has and only has one parent loader.

Next, I also draw a picture of the main process of class loading from the perspective of JDK underlying source code

These are the important steps of class loader loading a class. I hope you guys can combine the source code and study it carefully. In fact, it’s quite understandable.

Next, let’s talk about the advantages of using parental delegation to load classes in Java?

  • Benefits of the parental delegation model

    As we all know: java.lang.object Class is the parent of all classes, so our program will java.lang.object Class into memory, if java.lang.object class

    If it can be loaded by our custom class loader, there will be multiple class objects of objects in the JVM, and these class objects are not compatible.

    Therefore, the parental delegation model can ensure the security of types in Java core class library.

    With the help of parent delegation model, the classes of our Java core class library must be loaded by our boot loader, which can ensure that only one copy of our core class library exists in the JVM

    This will not give the custom class loader to load the classes of our core class library.

    According to our demonstration case, a class can be loaded by multiple class loaders, and there are multiple different versions of class objects in the memory of the JVM, which are incompatible.

    And it can’t be converted to each other.

What is full delegate loading?

Explanation: if our person class is loaded by our system class app class loader, and the dog class on which the person class depends will also be entrusted to the app system class for loading, this delegation process also follows the parent delegation model. The code is as follows

  • Create a dog instance in the person class code

public class Person {

public Person(){
  
      new Dog();
  }

}

public class Dog {

    public Dog(){
        System.out.println ("dog's constructor");
    }
}
  • Test class

    public class MainClass02 {
    
        public static void main(String[] args) throws Exception {
            //Create an instance of the custom class loader, and specify the name through the constructor
            Test01ClassLoader myClassLoader = new Test01ClassLoader("loader1");
            myClassLoader.setPath("I:\test\");
            Class> classz = myClassLoader.loadClass("com.test.Person");
            System.out.println(classz.getClassLoader());
            System.out.println(Dog.class.getClassLoader());
        }
    }
    
    
    Results of operation:
    
    [email protected]
    [email protected]
    
    Process finished with exit code 0

    From the above running results, we can see that when we use the custom class loader to load our person, according to the parent delegation model, our person is not loaded by the custom class loader (test01classloader), but by the appclassloader. At the same time, according to the overall delegation rules, our dog class is also loaded by the appclassloader. So we must remember this crucial conclusion. Lay a solid foundation for our later study.

Now let’s look at an example. We put the Person.class Delete the file, and then run the main function above to see the result. The code is as follows:

From that line of results, we can see that the person class is loaded by our custom class loader. Then why doesn’t the dog class fully delegate? This is because of the parental delegation model. There is no person class in our class path, so appclassloader can’t load our path I: \ \ test \ \ com.test.Person . class file. So the person class is loaded by our own class loader. Let’s look at the dog class again, because its loading should follow the parental delegation model, because there are two classes in the class path Dog.class So the appclassloader can load the dog class. Therefore, the classloader that loads the dog class is appclassloader. Write here, you have a very deep understanding of class loading. I believe it is self-evident why Java uses the parental delegation model. Now, let’s talk about the parental delegation model. Is there any disadvantage, or is there anything bad about it? Can we break this parental delegation to load classes? Let’s take a look at an example.

The namespace of the class loader

When it comes to the drawbacks of the parental delegation model, I can’t do without the concept of namespace.

The namespace of the class loader It is composed of the class loader itself and the binary name (full class name) loaded by all the parent loaders

① : two identical binary names are not allowed in the same namespace.

② : two identical binary names can appear in different namespace types. At that time, their corresponding class objects could not be perceived by each other, that is to say, the types of class objects were different.

Explanation: the same Person.class If the file is loaded by our different class loaders, two corresponding class objects of person will be generated in our JVM memory, and the two corresponding class objects are invisible to each other (the instance objects created by class object reflection are incompatible with each other and cannot be transformed into each other)**

③ : the class corresponding to the binary name in the child loader’s namespace can access the class corresponding to the binary name in the parent loader’s namespace, otherwise it can’t

Here is a picture for your understanding.

The above figure illustrates the concept of a namespace. You can have a good experience.

It’s not a strong evidence that we just draw pictures and talk. Just as I mentioned in this blog, when we learn and master a certain concept, we must produce strong evidence to prove our conjecture or point of view. Let’s give an example. Let’s see if our theory above is correct. The code is as follows:

This is the code for the person class.

package com.test;

public class Person {

    public Person() {
        new Dog();
        System.out.println (dog's CL assLoader:-- >"+  Dog.class.getClassLoader ());
    }

    static{
        System.out.println (the "person class is initialized");
    }
}

This is the code for the dog class.

package com.test;

public class Dog {

    public Dog(){
        System.out.println ("dog's constructor");
    }
}

The specific verification idea is as follows: first, we put the class file of the person class into the loading directory of the bootloader (C: / / program files / Java / jdk1.8.0)_ This is the loading directory of the bootloader to load the person class.

Then, let’s let the dog class be loaded by the appclassloader. Then we access the dog class in the person class. See if the visit is successful.

Test environment: take our Person.class It is placed in the following file: C: program files: Java: jdk1.8.0_ In this directory, our Person.class It will be loaded by our bootloader, and our dog class will be loaded by appclassloader. If our person class references our dog class, it will throw an exception

Create the main method to test:

package com.test;

import java.lang.reflect.Method;

/**
 *JVM class loader Chapter 1
 *@ author time - time
 *Custom class loader -- namespace
 *The class loaded by the test parent cannot access the class loaded by the child loader.
 */
public class MainClass02 {

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

        System.out.println (class loader for person:+ Person.class.getClassLoader ());

        System.out.println (dog's class loader:+ Dog.class.getClassLoader ());

        Class> clazz = Person.class;
        clazz.newInstance();


    }
}

Results of operation:
    
"C:\Program Files\Java\jdk1.8.0_144\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar=59226:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar;I:\jvm\out\production\jvm-classloader" com.test.MainClass02
Class loader of person: null
Dog's classloader: sun.misc.Launcher [email protected]
The person class is initialized
Exception in thread "main" java.lang.NoClassDefFoundError: com/test/Dog
	at com.test.Person.(Person.java:7)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at java.lang.Class.newInstance(Class.java:442)
	at com.test.MainClass02.main(MainClass02.java:20)

Process finished with exit code 1

Summary: from the above code, we can see that when we go to a new dog instance in person, we do not create it successfully, but throw an exception in thread “main” java.lang.NoClassDefFoundError : COM / test / dog, which proves the above conclusion (the class loaded by the parent loader cannot access the class loaded by the child loader.)

That is, the class loaded by the bootloader cannot access the appclassloader loaded by the system classloader.

So surely someone will ask, can the class loaded by our child loader access the class loaded by the parent loader? We might as well confirm that we just need to change the code of mainclass02, let the appclassloader load the dog class, and let our custom class loader load our person class. And access the dog class in the person class. Then, the previous_ Delete the class file in the person in the 131 / JRE / classes directory. In addition, delete the person file in our class path, and add it in the I: \ com.test.Person . class file. The code is as follows:

package com.test;

import java.lang.reflect.Method;

/**
 *JVM class loader Chapter 1
 *@ author time - time
 *Custom class loader
 *Test whether the class loaded by the child loader can access the class loaded by the parent loader.
 */
public class MainClass02 {

    public static void main(String[] args) throws Exception {
        //Create an instance of the custom class loader, and specify the name through the constructor
        Test01ClassLoader myClassLoader = new Test01ClassLoader("loader1");
        myClassLoader.setPath("I:\test\");
        Class> classz = myClassLoader.loadClass("com.test.Person");
        System.out.println(classz.getClassLoader());

        System.out.println (dog's class loader:+ Dog.class.getClassLoader ());

        classz.newInstance();


    }
}

Results of operation:
"C:\Program Files\Java\jdk1.8.0_144\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar=60588:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar;I:\jvm\out\production\jvm-classloader" com.test.MainClass02
Your own class loader has been loaded
[email protected]
Dog's classloader: sun.misc.Launcher [email protected]
The constructor of dog

Process finished with exit code 0

From the above results, we can see that person is loaded by our test01classloader custom class loader, then its father loader is appclassloader, obviously the dog class is loaded by our appclassloader. Therefore, the code runs normally without exception

1: Class loaded by parent loader cannot access class loaded by child loader.

2: The class loaded by the child loader can access the class loaded by the parent loader.

Disadvantages of parental delegation model

  • Let’s take a look at a very familiar code fragment related to database connection.

    Class.forName("com.mysql.jdbc.Driver");
    Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/RUNOOB","root","123456");
    Statement stmt = conn.createStatement();

case analysis

  • Why is the fifth step in the above figure loaded with thread context loader?
  • Under the mechanism of parental delegation model, class loading is from bottom to top. That is, the lower loader will delegate the upper loader to load. Some interfaces are Java core libraries( rt.jar )The Java core library is loaded by the boot loader, for example, the createstatement interface above. These interfaces are implemented by different vendors (MySQL). The specific implementation is put under classpath of our project by relying on jar package. Java’s boot class loader / root class loader does not load these jar packages from other sources.
  • We all know that the jar package under classpath is loaded by our system class loader / Application Loader. According to the mechanism entrusted by our parents, the parent class loader cannot see the specific implementation loaded by the subclass (system class loader). The interface of createstatement is loaded by the root class loader, but the specific implementation cannot. Under the mechanism of parental delegation, there is no specific implementation for the interface of createstatement.
  • We Java developers can load interface implementation classes by setting context loader to the current thread pool. In other words, the parent loader can use the current thread context loader to load the implementation of some interfaces that the parent loader cannot load. The SPI model (the interface is defined in the core library, and the implementation is dependent on our project in the form of jar) is perfectly solved.

Below I provide a flow chart of SPI. I don’t know what is SPI’s companion. Take a look at this picture

From the above example, we can see the disadvantages of the parental delegation model. Then our JDK provides us a way to break the rule of parental delegation by modifying the thread context class loading. About the topic of modifying context class loading, we will explain it in detail in the next chapter. Next, let’s take a look at several methods to get class loaders. The translated Java doc document is also provided. It is convenient for us to learn thread class loader.

Several methods of getting class loader

  • Class.getClassLoader()
/**
*Returns the class loader for the class
*Null to represent the bootstrap class loader
* This method will return null in such implementations if this class was loaded by the bootstrap class loader.
*If this method returns null, the class is loaded by our bootloader
*
* If this object represents a primitive type or void, null is returned.
(if the class of primitive type such as int, long, etc. or void type, then their classloader is null)
*
*
*/
public ClassLoader getClassLoader() {
ClassLoader cl = getClassLoader0();
if (cl == null)
return null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
ClassLoader.checkClassLoaderPermission(cl, Reflection.getCallerClass());
}
return cl;
}
  • 1: Returns the class loader that loads the class

  • 2: There are some virtual machines (such as hotspot) whose bootloader is null

  • 3: Primitive types such as int, long or void have null class loaders

  • ClassLoader.getSystemClassLoader Interpretation of () method

/**
*Returns the system class loader for delegation. This is the default
*Delegating parent for new classloader instances, and is
*Typically the class loader used to start the application
*
* This method is first invoked early in the runtime's startup
*At which point it creates the system class loader and sets it
*As the context class loader of the invoking thread
*
*  The default system class loader is an implementation-dependent
*Instance of this class
*
*  If the system property "java.system.class.loader" is defined
* when this method is first invoked then the value of that property is
* taken to be the name of a class that will be returned as the system
* class loader. The class is loaded using the default system class loader
* and must define a public constructor that takes a single parameter of
* type ClassLoader which is used as the delegation parent. An
* instance is then created using this constructor with the default system
* class loader as the parameter. The resulting class loader is defined
* to be the system class loader.
We can go through it java.system.class . loader system property to specify the binary name of a custom class load as the new system class loader,
In our custom loading, we need to define a constructor with parameters. The parameter is classloader. Then our custom class loader will be regarded as the system class loader

*
* @return The system ClassLoader for delegation, or
* null if none
*
* @throws SecurityException
* If a security manager exists and its checkPermission
* method doesn't allow access to the system class loader.
*
* @throws IllegalStateException
* If invoked recursively during the construction of the class
* loader specified by the "java.system.class.loader"
* property.
*
* @throws Error
* If the system property "java.system.class.loader"
* is defined but the named class could not be loaded, the
* provider class does not define the required constructor, or an
* exception is thrown by that constructor when it is invoked. The
* underlying cause of the error can be retrieved via the
* {@link Throwable#getCause()} method.
*
* @revised 1.4
*/
@CallerSensitive
public static ClassLoader getSystemClassLoader() {
//Initializes the system class loader
initSystemClassLoader();
if (scl == null) {
return null;
}
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkClassLoaderPermission(scl, Reflection.getCallerClass());
}
return scl;
}
  • 1: This method returns the system class loader
  • 2: It is also the direct parent of our custom loader
  • 3: The system class loader is used to start our application
  • 4: In the early days of the system, the calling thread will create our system class loader and set it to the context of the current thread
  • 5: We can use the system properties: java.system.class . loader to specify our custom class loader to act as our system class loader, but our custom loader needs to provide a constructor with parameters (classloader)

This article is written here. The art of JVM will continue to be serialized. Interested readers can pay attention to me:

The art of JVM class loader (1) is over

The art of JVM class loader (2) is over

The art of JVM class loader (3) in creation

Author’s official account: geek time

Recommended Today

Go carbon version 1.2.0 has been released to improve and optimize the multi scenario support for orm

Carbon is a lightweight, semantic and developer friendly golang time processing library, which supports chain call, lunar calendar, Gorm, xorm and other mainstream orm If you think it’s good, please give it to a star github:github.com/golang-module/carbon gitee:gitee.com/go-package/carbon Installation and use //Using GitHub Library go get -u github.com/golang-module/carbon import ( “github.com/golang-module/carbon”) //Using gitee Library go get […]