Java virtual machine class loader and parents delegate mechanism

The so-called class loader (Class Loader) is to load Java classes into the Java virtual machine, in front of " the interviewer, do not ask me again" Java Virtual Machine class loading mechanism, "a " mechanism has been introduced in the specific load class files. In this article we focus on the loader and parents delegate mechanism.

Class loader

There are three types in the JVM configured ClassLoader: Start the class (or the root class) loader (Bootstrap ClassLoader), extended class loader (ExtClassLoader), application class loader (AppClassLoader). Different class loader is responsible for loading classes in different regions.

image

Start class loader: This is not a Java class loader, but by the underlying ++ implementation of c, is responsible for the library will be stored in the lib directory JAVA_HOME, such as rt.jar. Therefore, the boot class loader is not part of the Java class libraries, the Java program can not be directly referenced when users write custom class loader, if you need to load the delegates to the bootstrap class loader that can be used directly instead of null.

Extension class loader: realized by sun.misc.Launcher $ ExtClassLoader, is responsible for loading the JAVA_HOME under libext directory, or by all libraries, developers java.ext.dirs system variables specified path can be used directly extended by Loader.

Application class loader: the sun.misc.Launcher $ AppClassLoader achieve. Since the class loader is the return value of the ClassLoader getSystemClassLoader method, also called the system class loader. It is responsible for loading the user class library specified by the path, it can be used directly. If no custom class loader, default is the class loader.

You can print load paths and related jar in this way:

System.out.println("boot:" + System.getProperty("sun.boot.class.path"));
System.out.println("ext:" + System.getProperty("java.ext.dirs"));
System.out.println("app:" + System.getProperty("java.class.path"));复制代码

In the printed journal, you can see detailed route and the path which contains the following libraries. As more print content, there is not demonstrated.

Class loader initialization

In addition to the boot class loader, loading the extension class loader and an application class is the class of sun.misc.Launcher initializing, by the Launcher class loader to load the root class. Related code is as follows:

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);
    
   //...
}复制代码

Parent delegation model

Parent delegation model: a class loader when the class loader receives the request, it first asks its parent class loader, is sequentially and recursively, if the parent class loader when the class can not be found (in accordance with the fully qualified name of the class), subclass loader will attempt to load.

image

Parent-child relationship is generally delegated parents will not inherit the way to achieve, and are using a combination of complex code in relation to the parent loader.

By writing the test code, for Debug, the relationship can be found between the combination of different process parent class loader delegate.

image

And this process of borrowing a timing diagram to see a lot cleaner.image

Source loadClass

ClassLoader class is an abstract class, but it does not contain any abstract methods. ClassLoader class and override inherited findClass method can implement a custom class loader. However, if the destruction of the above parent delegation model to implement custom class loader, you need to inherit ClassLoader class and override loadClass method and findClass method.

Source portion ClassLoader class as follows:

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) {
                    //如果当前类没有被加载且父类加载器不为null,则请求父类加载器进行加载操作
                    c = parent.loadClass(name, false);
                } else {
                   //如果当前类没有被加载且父类加载器为null,则请求根类加载器进行加载操作
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
            }

            if (c == null) {
                long t1 = System.nanoTime();
               //如果父类加载器加载失败,则由当前类加载器进行加载,
                c = findClass(name);
                //进行一些统计操作
               // ...
            }
        }
        //初始化该类
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}复制代码

The above mention is also the code and the hierarchical relationship between the different combinations of class loaders.

Why use parent delegation model

Parent delegation model is to ensure type-safe Java core libraries. All Java applications need to reference at least java.lang.Object class, this class at runtime that need to be loaded into the Java virtual machine. If the custom class loading process done by the loader, there are several versions it might java.lang.Object class, and among these classes are not compatible.

By parent delegation model for loading of Java core libraries working class to unify done by the start of the class loader to ensure that the classes are Java applications use the same version of Java core libraries are compatible with each other.

Context class loader

Subclass loader retains a reference to the parent class loader. However, if the parent class loader loads the class needs to access child classes loaded in how to deal with? The most classic scene JDBC is loaded.

JDBC is a set of Java to develop a standard interface to access the database, which is included in Java foundation class library, loaded by the root class loader. And each vendor's database is implemented as a third party library introduced the use-dependent, in part, is achieved library loaded by the application class loader.

Get code Mysql connection:

//加载驱动程序
Class.forName("com.mysql.jdbc.Driver");
//连接数据库
Connection conn = DriverManager.getConnection(url, user, password);复制代码

DriverManager class is loaded by the boot loader, which uses the database-driven (com.mysql.jdbc.Driver) by the application class loader, which is typical of the parent class loader loads the class needs to be loaded by subclasses access loaded classes.

Realization of this process, see the source code DriverManager class:

//建立数据库连接底层方法
private static Connection getConnection(
        String url, java.util.Properties info, Class<?> caller) throws SQLException {
    //获取调用者的类加载器
    ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
    synchronized(DriverManager.class) {
        //由启动类加载器加载的类,该值为null,使用上下文类加载器
        if (callerCL == null) {
            callerCL = Thread.currentThread().getContextClassLoader();
        }
    }

    //...

    for(DriverInfo aDriver : registeredDrivers) {
        //使用上下文类加载器去加载驱动
        if(isDriverAllowed(aDriver.driver, callerCL)) {
            try {
                //加载成功,则进行连接
                Connection con = aDriver.driver.connect(url, info);
                //...
            } catch (SQLException ex) {
                if (reason == null) {
                    reason = ex;
                }
            }
        } 
        //...
    }
}复制代码

In the above code, attention diverted Code:

callerCL = Thread.currentThread().getContextClassLoader();复制代码

This line of code gets ContextClassLoader from the current thread, and ContextClassLoader where to set it? It is set in the above Launcher in the source code:

// 设置上下文类加载器
Thread.currentThread().setContextClassLoader(this.loader);复制代码

As a result, a so-called context class loader is essentially application class loader. Therefore, the context class loader just to solve a concept of reverse access the class put forward, is not a new class loader, in essence, is the application class loader.

Custom class loader

Custom class loader needs to inherit only java.lang.ClassLoader class, and then override findClass (String name) method may be, how to obtain the specified bytecode stream class in the process.

If the delegating parents to destroy the foundation would need to override loadClass method (parent delegated specific logic implementation). But not recommended.

public class ClassLoaderTest extends ClassLoader {

    private String classPath;

    public ClassLoaderTest(String classPath) {
        this.classPath = classPath;
    }

    /**
     * 编写findClass方法的逻辑
     *
     * @param name
     * @return
     * @throws ClassNotFoundException
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // 获取类的class文件字节数组
        byte[] classData = getClassData(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        } else {
            // 生成class对象
            return defineClass(name, classData, 0, classData.length);
        }
    }

    /**
     * 编写获取class文件并转换为字节码流的逻辑
     *
     * @param className
     * @return
     */
    private byte[] getClassData(String className) {
        // 读取类文件的字节
        String path = classNameToPath(className);
        try {
            InputStream is = new FileInputStream(path);
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            byte[] buffer = new byte[2048];
            int num = 0;
            // 读取类文件的字节码
            while ((num = is.read(buffer)) != -1) {
                stream.write(buffer, 0, num);
            }
            return stream.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 类文件的完全路径
     *
     * @param className
     * @return
     */
    private String classNameToPath(String className) {
        return classPath + File.separatorChar
                + className.replace('.', File.separatorChar) + ".class";
    }

    public static void main(String[] args) {
        String classPath = "/Users/zzs/my/article/projects/java-stream/src/main/java/";
        ClassLoaderTest loader = new ClassLoaderTest(classPath);

        try {
            //加载指定的class文件
            Class<?> object1 = loader.loadClass("com.secbro2.classload.SubClass");
            System.out.println(object1.newInstance().toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}复制代码

Print Results:

SuperClass static init
SubClass static init
com.secbro2.classload.SubClass@5451c3a8复制代码

About SuperClass and SubClass in the article, " the interviewer, do not ask me again" Java Virtual Machine class loading mechanism, "a " has posted the code, there is no longer posted.

As can be seen by the above code, the major rewrite of the path will findClass acquired class implements custom class loader.

So, what scenario will use a custom class loader it? When the JDK class loader implementation can not meet our needs, it needs to implement its own class loader. For example, OSGi, hot code field deployment.

Java9 class loader modifies

Above class loader model Java8 previous versions, in Java9 class loader it has changed. Here briefly explain the main changes related models, the specific details of the changes are no longer here started.

Java9 change in the directory.image

Changing Java9 class loader.image

In java9, application class loader can be delegated to the platform class loader and a boot class loader; internet class loader can be delegated to the boot class loader and application class loader.

In java9, boot class loader and the code library is implemented in a virtual machine. For backward compatibility, the program is still represented by a null. For example, Object.class.getClassLoader () still returns null. However, not all JavaSE platform and JDK module consists of the boot class loader loads.

A few examples, boot class loader module is java.base, java.logging, java.prefs and java.desktop. Other platforms and JDK JavaSE internet module loaded by class loader and application class loader.

java9 no longer supported for specifying boot classpath, -Xbootclasspath and -Xbootclasspath / p options and system properties sun.boot.class.path. -Xbootclasspath / a option is still supported, the value stored in the system's properties jdk.boot.class.path.append.

java9 extension mechanism is no longer supported. However, it will remain in the extension class loader platform under the new name with the class loader. ClassLoader class includes a static method called getPlatformClassLoader (), the method returns a reference to the class loader of the platform.

summary

This article is mainly based on java8 it introduced the Java virtual machine class loader delegate mechanism and parents, and some changes in Java8. Which, java9 in deeper change, we can further study. The series is continuously updated, the welcome attention to micro-channel public number "New Horizons program."

Original link: " the Java virtual machine class loader and parents delegate mechanism "

"Interviewer" series:


New Horizons program : exciting and growth are not to be missed

New Horizons program - micro-channel public number

Guess you like

Origin juejin.im/post/5db7ea356fb9a0204924fda1