Three kinds of loaders

Bootstrap ClassLoader

The

\lib directory, or the path specified by the -xbootclasspath parameter, is loaded into the memory of the virtual machine. This class loader is implemented by C++ at the bottom and is part of the virtual machine. The other classloaders are implemented in Java, independent of the virtual machine, and all inherit from the java.lang.ClassLoader abstract class.

When you start the classloader, an important class, Sun.misc.Launcher, is loaded with two static inner classes:

  1. ExtClassLoaderExtend the class loader
  2. AppClassLoaderApplication loader

After the class Launcher is loaded, the class is initialized. During initialization, two instances of class loaders are created, with the source code as follows:

public Launcher(a) {
    Launcher.ExtClassLoader var1;
    try {
        var1 = Launcher.ExtClassLoader.getExtClassLoader();
    } catch (IOException var10) {
        throw new InternalError("Could not create extension class loader", var10);
    }

    try {
        this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
    } catch (IOException var9) {
        throw new InternalError("Could not create application class loader", var9); }}Copy the code

Extension ClassLoader

Is responsible for loading all libraries in the

\lib\ext directory, or in the path specified by the java.ext.dirs system variable.

Application ClassLoader

Also known as the “system class loader,” it is responsible for loading all the class libraries on the user’s classpath (Java-classpath). If there is no custom class loader, this loader is the default class loader. An instance of the ClassLoader can be obtained from the ClassLoader’s getSystemClassLoader().

Note: The class information loaded by each class loader is stored in a different area of the method area, so if different class loaders load the same class bytecode file, the resulting two class objects will look different to the virtual machine! If there are two identical User class objects, the two User class objects loaded by Application ClassLoader and Extension ClassLoader are not the same.

Parental delegation model

The work process assigned by parents

A class loader received class loading request, it will not load the right away, but delegates the request to parent to complete, each level of class loaders, so all the class loading request must go through to start the class loader attempts to load and only when the parent loader cannot load this class, Then it passes the load request to its child loader to try loading, as follows:

The role of parental delegation models

One of the obvious benefits of using the parent-delegate model to organize relationships between class loaders is that classes in Java have a hierarchy of priorities along with their loaders. For example, java.lang.Object is stored in rt.jar. No matter which layer of class loader needs to load the Object class, it is ultimately delegated to the launcher class loader to load it, so it is guaranteed that the Object class is the same class in the various class loader environments of the program.

Loading the same Class by different classloaders will result in the same bytecode file producing different Class instance information. That is, the same bytecode file loaded by each loader will be stored in different places in the method area, so if the parent delegate model is not used, if the user writes a java.lang.Object Class, Put it in your classpath, and you’ll have multiple versions of The Object Class in your system, and your program will be in a mess, using The Object Class without knowing which loader is loading the Class instance.

In the figure below, each loader has its own area in the method area to store information about the class types it loads, so this is where the parent-delegate model is important.

Source code implementation of the parent delegate model

protectedClass<? > loadClass(String name,boolean resolve) 
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // First check to see if the class is already loadedClass<? > c = findLoadedClass(name);if (c == null) {
            long t0 = System.nanoTime();
            try {
                if(parent ! =null) {
                    // Pass it to the parent loader to try to load the class
                    c = parent.loadClass(name, false);
                } else{ c = findBootstrapClassOrNull(name); }}catch (ClassNotFoundException e) {
                // If the parent loader throws a ClassNotFoundException, the parent loader cannot load the class
            }

            if (c == null) {
                // If the class is not loaded, call the findClass method in order to try to load the class
                long t1 = System.nanoTime();
                c = findClass(name);

                // Record the loading information for this classsun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); }}if (resolve) {
            resolveClass(c);
        }
        returnc; }}Copy the code

Characteristics of the parental delegation model

  1. The class information stored in the method area of the parent loader is visible to the child loader, because the child loader will first delegate to the parent loader after receiving the class loading request. If the parent loader has already loaded the class, it will directly return the information of the class to the child loader, so that the child loader does not need to continue loading.
  2. Under the parent delegate model, each class is unique and loaded by only one class loader.

Custom class loaders that violate parental delegation

We can inherit the ClassLoader class, override loadClass() without delegating to the parent loader in our code, and override the findClass() method to define its own classloading rules.

We define a class that is the same as the system class library, sun. Applet.Main, and use a custom class loader to load the class.

The structure diagram of the test Demo is as follows, create the project according to the structure diagram, copy the code into it, there must be no mistake, once the author was stuck in finding the classpath all night

Step Y, customize the Sun.applet. Main class

package sun.applet;

/ * * *@author Zeng
 * @date2020/4/10 8:22 * /
public class Main {
    static {
        System.out.println("customized sun.applet.Main constructed");
    }
    public static void main(String[] args) {
        System.out.println("recognized as sun.applet.Main in jdk," +
                " and there isn't any main method"); }}Copy the code

Step 2, customize the UnDelegationClassLoader

package sun.applet;

import java.io.*;

/ * * *@author Zeng
 * @date2020/4/10 8:01 * /
public class UnDelegationClassLoader extends ClassLoader {

    private String classpath;

    public UnDelegationClassLoader(String classpath) {
        super(null);
        this.classpath = classpath;
    }
    @Override
    protectedClass<? > findClass(String name)throws ClassNotFoundException {
        InputStream is = null;
        try {
            String classFilePath = this.classpath + name.replace("."."/") + ".class";
            is = new FileInputStream(classFilePath);
            byte[] buf = new byte[is.available()];
            is.read(buf);
            return defineClass(name, buf, 0, buf.length);
        } catch (IOException e) {
            throw new ClassNotFoundException(name);
        } finally {
            if(is ! =null) {
                try {
                    is.close();
                } catch (IOException e) {
                    throw newIOError(e); }}}}@Override
    publicClass<? > loadClass(String name)throwsClassNotFoundException { Class<? > clz = findLoadedClass(name);if(clz ! =null) {
            return clz;
        }
        // The JDK currently adds permission protection for packages starting with "Java.", which we are still handing to the JDK to load
        if (name.startsWith("java.")) {
            return ClassLoader.getSystemClassLoader().loadClass(name);
        }
        return findClass(name);
    }
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, FileNotFoundException {
        sun.applet.Main obj1 = new sun.applet.Main();
        UnDelegationClassLoader unDelegationClassLoader = new UnDelegationClassLoader("./out/production/classloading/");
        System.out.println(unDelegationClassLoader.findClassPath());
        String name = "sun.applet.Main"; Class<? > clz = unDelegationClassLoader.loadClass(name); Object obj2 = clz.newInstance(); System.out.println("obj1 class: "+obj1.getClass());
        System.out.println("obj2 class: "+obj2.getClass());
        System.out.println("obj1 classloader: "+obj1.getClass().getClassLoader());
        System.out.println("obj2 classloader: "+obj2.getClass().getClassLoader());
        System.out.println("obj1 == obj2"+ obj1 == obj2); }}Copy the code

The output is as follows

As you can see, obj1, an instance of Main in the Java class library, and obj2, a custom instance of Obj2, both have class objects of Sun.applet. Main, but obj2 is not the same as the Main object in the Java class library because of the class loaders. So without parental delegation, if a third party dependent needs to call Sun.applet. Main, it becomes confusing and doesn’t know which Main to call.

Correct posture for customizing class loaders

Custom class loader DelegationClassLoader and delegate to parent loader AppClassLoader

package sun.applet;

import java.io.FileInputStream;
import java.io.IOError;
import java.io.IOException;
import java.io.InputStream;

/ * * *@author Zeng
 * @date2020/4/10 "* /
public class DelgationClassLoader extends ClassLoader {

    private String classpath;

    public DelgationClassLoader(String classpath, ClassLoader parent) {
        super(parent);
        this.classpath = classpath;
    }

    @Override
    protectedClass<? > findClass(String name)throws ClassNotFoundException {
        InputStream is = null;
        try {
            String fileClasspath = this.classpath + name.replace("."."\ \") + ".class";
            is = new FileInputStream(fileClasspath);
            byte[] b = new byte[is.available()];
            is.read(b);
            return defineClass(name, b, 0, b.length);
        }catch (Exception e){
            throw new ClassNotFoundException(name);
        }finally {
            if(is ! =null) {
                try {
                    is.close();
                } catch (IOException e) {
                    throw new IOError(e);
                }
            }
        }

    }
}

Copy the code

The second step is to test loading the same name class sun.applet.Main. Due to space problems, only the core method is posted

 public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, FileNotFoundException {
        sun.applet.Main obj1 = new sun.applet.Main();
        DelgationClassLoader unDelegationClassLoader = new DelgationClassLoader("./out/production/classloading/", ClassLoader.getSystemClassLoader());
        String name = "sun.applet.Main"; Class<? > clz = unDelegationClassLoader.loadClass(name); Object obj2 = clz.newInstance(); System.out.println("obj1 class: "+obj1.getClass());
        System.out.println("obj2 class: "+obj2.getClass());
        System.out.println("obj1 classloader: "+obj1.getClass().getClassLoader());
        System.out.println("obj2 classloader: "+obj2.getClass().getClassLoader());
        System.out.println("obj1 instanceof sun.applet.Main: " + (obj1 instanceof sun.applet.Main));
        System.out.println("obj2 instanceof sun.applet.Main: " + (obj2 instanceof sun.applet.Main));
    }
Copy the code

The output result is as follows:

As you can see, both the Sun.applets.Main in the Java class library and the custom Sun.applets.Main are handed over to the startup class loader by the JVM, so it knows very well when a third party dependency wants to call sun.applets. Only one Class object can be called. So this also explains why the parental delegation model can determine that there is only one unique class in the system.

Conclusion:

This article mainly explains the three default class loaders, what is the parent delegate model, the work flow of the parent delegate model, source code analysis and its characteristics, using a practical case to demonstrate the consequences of violating the parent delegate model, reflecting the benefits of the parent delegate model. Of course, the parent delegation model is not a mandatory constraint, it is just one of the implementation of virtual machines, such as Tomcat, JDBC and other technologies destroyed the parent delegation model, interested readers can go to in-depth understanding, the author due to limited ability, limited time, can not explain more detailed in-depth knowledge points. If this article is of little help to you, your likes are the biggest encouragement and support for me! Thanks for reading!

Shoulders of giants:

In Depth understanding the Java Virtual Machine, third edition

Juejin. Cn/post / 684490…

Blog.csdn.net/lengxiao199…