The parent class loader delegation model Detailed

This article first appeared personal website, For reprint please indicate the source: the class loader delegation model parents, to see this is enough

In the previous article, we combed the basic concept of the class loader : class life cycle, the role of class loader, class loading and unloading time, and so on, then this article we continue to review the text before class loader knowledge, including: What are the JVM class loader have? What is the relationship between them? What is a parent to delegate mechanism?

Parent delegation model

Four types loader

From the perspective of the JVM, class loader there are two categories: Bootstrap ClassLoader and other types of load, Bootstrap ClassLoader is the C ++ language, is part of the virtual machine itself; other Java class loader is language, not a virtual machine, all derived from the abstract class java.lang.ClassLoader.

From the Java developer's perspective, it is necessary to know the parent class loader delegation model, as shown below:

Parent delegation model

  • Bootstrap ClassLoader: Start class loader, the class loader will be responsible for store in / Lib directory, -Xbootclasspath parameters are specified path and opportunity recognition jar virtual library is loaded into memory. More bluntly put, it is that we often class java.lang beginning, it must be loaded Bootstrap ClassLoader.

  • Extension ClassLoader: extension class loader, the class loader is implemented by sun.misc.Launcher $ ExtClassLoader, it is responsible for loading / Lib / ext directories, or libraries java.ext.dirs all system variables are specified path.

  • Application ClassLoader: application class loader, the class loader is implemented by sun.misc.Launcher $ AppClassLoader, which is responsible for all library path loads CLASSPATH user environment variables specified in. If the application does not live their custom class loader, this is a Java program in the default class loader.

  • User-defined class loader: the user in case of need, can be achieved own custom class loader, in general, require custom class loader in the following cases: (1) isolation load class. Certain framework in order to achieve the isolation module middleware and applications, middleware, and applications need to use a different class loader; (2) modify the class loaded fashion. The parent class loading delegation model is not mandatory, the user classes can be dynamically loaded as needed at some point in time; (3) the extension class loader sources, for example, the class is loaded from the database network; (4) the source code to prevent leakage. Java code can easily be decompiled and tampering, in order to prevent leakage of source code, can be encrypted byte code file class and write a custom class loader to load your application class.

Example 1: a different class loader

In the following code, the java.util.HashMap rt.jar is included in the category, so that the class loader is null, DNSNameService class is placed in the jar package ext directory class, so its classloader It is ExtClassLoader; MyClassLoaderTest class loader is the application class loader.

import java.util.HashMap;

import sun.net.spi.nameservice.dns.DNSNameService;

public class MyClassLoaderTest {

    public static void main(String[] args) {

        System.out.println("class loader for HashMap: " + HashMap.class.getClassLoader());
        System.out.println(
            "class loader for DNSNameService: " + DNSNameService.class.getClassLoader());
        System.out.println("class loader for this class: " + MyClassLoaderTest.class.getClassLoader());
        System.out.println("class loader for Blob class: " + com.mysql.jdbc.Blob.class.getClassLoader());

    }
}

Access through the above-described code shown in FIG run:

image-20191013141845449

Example 2: Path Management different class loader

By following this program, you can see, each class loader is responsible for the jar file paths are not the same:

public class JVMClassLoader {

    public static void main(String[] args) {
        System.out.println("引导类加载器加载路径:" + System.getProperty("sun.boot.class.path"));
        System.out.println("扩展类加载器加载路径:" + System.getProperty("java.ext.dirs"));
        System.out.println("系统类加载器加载路径:" + System.getProperty("java.class.path"));
    }
}

image-20191013140720888

Examples 3: Arthas in order classloader

Arthas provided classloader command can be used to view the application class loader statistical information relating to the current, as shown below,

  1. After entering classloader show tables summarize the current class loader application, the number of the number of instances of each class loader, class loader loads each class.
  2. After entering classloader -t, shows the hierarchy of the current class loader application, it can be seen, BootStrap ClassLoader indeed top class loader system, followed by the extension class loader, then to the application class loader, where there is also a ArthasClassLoader, Arthas is a realization of their own custom class loader.

image-20191013135633962

Parents can delegate process model

如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。

使用双亲委派模型来组织类加载器之间的关系,有一个显而易见的好处就是Java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object,它存放在rt.jar之中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。相反,如果没有使用双亲委派模型,由各个类加载器自行去加载的话,如果用户自己编写了一个称为java.lang.Object的类,并放在程序的Class Path中,那系统中将会出现多个不同的Object类,Java类型体系中最基础的行为也就无法保证,应用程序也将会变得一片混乱。

双亲委派模型的实现非常简单,实现双亲委派的代码在java.lang.ClassLoader的loadClass()方法之中,如下面的代码所示:

    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 首先,检查该类是否已经被加载
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // 如果父类加载器抛出ClassNotFoundException,
                    // 说明父类加载器无法完成加载请求
                }

                if (c == null) {
                    // 在父类加载器无法加载的时候,再调用本类的findClass方法进行类加载请求
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    // 当前类加载器是该类的define class loader
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

破坏双亲委派模型

线程上下文加载器

如上所述,双亲委派模型很好得解决了各个类加载器的基础类的统一问题(越基础的类由越上层的加载器进行加载),如果基础类又要回调用户的类该怎么办?一个非常经典的例子就是SQL的驱动管理类——java.sql.DriverManager。

java.sql.DriverManager是Java的标准服务,该类放在rt.jar中,因此是由启动类加载器加载的,但是在应用启动的时候,该驱动类管理是需要加载由不同数据库厂商实现的驱动,但是启动类加载器找不到这些具体的实现类,为了解决这个问题,Java设计团队提供了一个不太优雅的设计:线程上下文加载器(Thread Context ClassLoader)。这个类加载器可以通过java.lang.Thread类的setContextClassLoader()方法进行设置,如果创建线程时候它还没有被设置,就会从父线程中继承一个,如果再应用程序的全局范围都没有设置过的话,那这个类加载器就是应用程序类加载器。

有了线程上下文加载器,就可以解决上面的问题——父类加载器需要请求子类加载器完成类加载的动作,这种行为实际上就是打破了双亲委派的加载规则。

Thread context loader

源码分析

接下来,我们以java.sql.DriverManager为例,看下线程上下文加载器的用法,在java.sql.DriverManager类的下面这个静态块中,是JDBC驱动加载的入口。

    /**
     * Load the initial JDBC drivers by checking the System property
     * jdbc.properties and then use the {@code ServiceLoader} mechanism
     */
    static {
        loadInitialDrivers();
        println("JDBC DriverManager initialized");
    }

顺着loadInitialDrivers()方法往下看,使用线程上下文加载器的地方在ServiceLoader.load里

    private static void loadInitialDrivers() {
                // ……省去别的代码
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
                            
                ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                Iterator<Driver> driversIterator = loadedDrivers.iterator();
              
                try{
                    while(driversIterator.hasNext()) {
                        driversIterator.next();
                    }
                } catch(Throwable t) {
                // Do nothing
                }
                return null;
            }
        });
      //…… 省去别的代码

ServiceLoader.load方法的代码如下,JDBC的sqlDriverManager就是这里获得的上下文加载器来驱动用户代码加载指定的类的。

    public static <S> ServiceLoader<S> load(Class<S> service) {
          // 获取当前线程中的上下文类加载器
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }

那么这个上下文加载器是什么时候设置进去的呢?前面我们提到了:

这个类加载器可以通过java.lang.Thread类的setContextClassLoader()方法进行设置,如果创建线程时候它还没有被设置,就会从父线程中继承一个,如果再应用程序的全局范围都没有设置过的话,那这个类加载器就是应用程序类加载器。

看下setContextClassLoader()方法别谁调用了,最终我们在Launcher中找到了如下代码:

public class Launcher {
        //……省去别的代码
    public Launcher() {
        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);
        }

        Thread.currentThread().setContextClassLoader(this.loader);
        //……省去别的代码
    }
}

总结

In this article we review the parent class loader delegation model, parents delegate the work process model, and source code analysis as well as the need for parents to break the delegation model. At the end of the first part, we also demonstrate the usage of Arthas class loader command to troubleshoot problems when they actually consider using.

Reference material

  1. https://www.journaldev.com/349/java-classloader#java-classloader-hierarchy
  2. https://www.cnblogs.com/joemsu/p/9310226.html
  3. "In-depth understanding of the Java Virtual Machine"
  4. "Code of efficient Java Development Manual"

This focus on the number of back-end technology, JVM troubleshooting and optimization, Java interview questions, personal growth and self-management, and other topics, providing front-line developers work and growth experience for the reader, you can expect to gain something here.Jwadu

Guess you like

Origin www.cnblogs.com/javaadu/p/11668572.html