JVM class loading mechanism (2) [class loader and parent delegation model]

Class Loaders and the Parent Delegation Model


Foreword: Start by listing the three questions I mentioned earlier. 1. When will the JVM load the Class file and initialize the class? 2. How does the jvm load the Class file? 3. What are the steps for jvm to load a Class file? In the previous blog post, we have explained the first problem , the timing of class loading, and this article focuses on the second problem.

class loader


In the first stage of class loading, the "loading" stage, the fully qualified name of a class is required to obtain the binary byte stream that defines this class, and the class loader is what completes this action. This action is implemented outside the Java virtual machine, allowing the application to decide how to obtain the required classes.

There are three types of class loaders predefined by the JVM:

  • Bootstrap class loader: It is a class loader implemented with native code, which is responsible for loading the class library under Java_Home/lib into memory (such as rt.jar). Since the bootstrap class loader involves the local implementation details of the virtual machine, the developer cannot directly obtain the reference of the boot class loader, so direct operations by reference are not allowed.
  • Standard extension (Extension) class loader: It is implemented by Sun's ExtClassLoader (sun.misc.Launcher$ExtClassLoader). It is responsible for loading into memory Java_Home/lib/ext or the class library in the location specified by the system variable java.ext.dir. Developers can use the standard extension class loader directly.
  • System (System) class loader: It is implemented by Sun's AppClassLoader (sun.misc.Launcher$AppClassLoader). It is responsible for loading into memory the class libraries specified in the system classpath (CLASSPATH). Developers can use the system class loader directly.

In addition, there are custom class loaders, and the hierarchical relationship between them is called the parent delegation model of class loaders. This model requires that in addition to the top-level startup class loader, the rest of the class loaders should have their own parent class loader, and this parent-child relationship is generally achieved through the composition (Composition) relationship, rather than through inheritance (Inheritance).

Two-parent delegation model


Description of the parent delegation mechanism:
       when a specific class loader receives a request to load a class, it first delegates the loading task to the parent class loader, recursively, and returns successfully if the parent class loader can complete the class loading task; Only when the parent class loader cannot complete the loading task, it will load it by itself.
Parent delegation model

Below we can deeply understand the parent delegation mechanism from the source code:

 protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class c = findLoadedClass(name);
            if (c == null) {
                try {
                    if (parent != null) {
                        //如果没有被加载,就委派给父类加载器加载
                        c = parent.loadClass(name, false);
                    } else {
                        //若父加载器为空,则默认使用启动类加载器作为父加载器
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                    //如果抛出异常,说明父类加载器无法完成类加载请求
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    c = findClass(name);
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

Three custom class loaders

From the above analysis of the loadClass(String name, boolean resolve) method of java.lang.ClassLoader:

1. If you don't want to break the parent delegation model, you only need to rewrite the findClass method.

2. If you want to break the parent delegation model, then rewrite the entire loadClass method.

Of course, our custom ClassLoader doesn't want to break the parent delegation model, so the custom ClassLoader inherits from java.lang.ClassLoader and only overrides the findClass method.

package aboutJvm;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class MyClassLoader extends ClassLoader{

    //.class文件所在路径
    private String path = "F:\\test";

    public MyClassLoader(){

    }

    public MyClassLoader(ClassLoader parent){
        super(parent);
    }


    /**
     * 重写findClass方法
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] data = loadClassData(name);
        if(data == null){
            throw new ClassNotFoundException();
        }else{
            return this.defineClass(name, data, 0, data.length);

        }
    }

    /**
     * 获取类的class文件的字节数组 
     */
    public byte[] loadClassData(String className){

        String fileName = path + File.separatorChar
                + "Person.class";

        System.out.println("fileName: "+fileName);

        try {
            FileInputStream fis = new FileInputStream(new File(fileName));
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int b = 0;
            while((b = fis.read())!=-1){
                baos.write(b);
            }
            return baos.toByteArray();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch (IOException e) {
            e.printStackTrace();
        }

        return null;

    }

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        MyClassLoader myClassLoader = new MyClassLoader();
        Class<?> clazz = myClassLoader.loadClass("aboutJvm.Person");//和MyClassLoader类同包
        Object obj = clazz.newInstance();
        System.out.println("类加载器: "+obj.getClass().getClassLoader());
    }
}
/**
OutPut:
    类加载器:sun.misc.Launcher$AppClassLoader@5736ab79
*/

       It can be seen from the output that the program does not call our own defined class loader. This is because there is Person.class under CLASSPATH, so it is naturally the Application ClassLoader to load the .class file.
      Solution:

  • Copy the compiled Person.class file to the F:\test directory, and delete Person.class under CLASSPATH. At this point, the Application ClassLoader will hand over the .class file to the next-level user-defined ClassLoader to load.
  • The first line in the main method is written as follows:
    MyClassLoader myClassLoader = new MyClassLoader(ClassLoader.getSystemClassLoader().getParent());
    that is, set the parent loader of the custom ClassLoader to Extension ClassLoader, so that the parent loader cannot load Person. class, it will be loaded by the sub-loader MyClassLoader.
此时output:
    fileName: F:\test\Person.class
    类加载器: aboutJvm.MyClassLoader@2d7fc1e7

Problems with writing custom class loader: java.lang.NoClassDefFoundError, this blog post solved my problem very well.
https://www.cnblogs.com/chenjfblog/p/7904024.html

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324939671&siteId=291194637