Interview must see - Java class loader (custom class loader)

1, Role of class loader

Java code cannot be run directly. It needs to be compiled into binary bytecode files recognized by the JVM through the compiler, and the function of class loader is to convert these binary bytecode files, namely The class file is loaded into the virtual machine so that the virtual machine can run the program.

2, Java virtual machine class loader structure

1. Boot class (boot class) loader

Bootstrap class loader: also known as root class loader or original class loader. Bootstrap class loader is a class loader implemented by local code (C language), which is responsible for Java_ The core class libraries under home / JRE / lib, such as rt.jar and resource Jar or load the jar package and other class libraries recognized by the JVM in the directory specified by the - Xbootclasspath parameter into memory. The loader is recognized in the Lib directory with the file name. For example, if the file name is not rt.jar, it will not be loaded even if it is placed in the Lib directory. Since the startup class is implemented by C language, developers cannot directly obtain the reference of the startup class loader. Therefore, the path to which the boot class loader is loaded can be determined by system Getproperty ("sun.boot.class.path") view.
Expansion: the boot class is the same as other class loaders, but it is not a Java class. In Java, all classes must be loaded into memory. The extension class, that is, the system class loader, also needs to be loaded. When the JVM is started, it will run special machine code to load the system class loader and the extension class loader, This special machine instruction will start the whole class loading process. It is his job to boot the class loader and load a pure Java class loader. In a word, the boot class loader is written in c + + and started by the JVM. The boot class loader is also responsible for loading all the code required to support the basic Java runtime environment, including Java Util and Java Class under Lang package.

2. Extension class loader

Extension class loader: the extension class loader is implemented by Sun's ExtClassLoad, which is Sun misc. Luncher's static inner class. It is responsible for Java_ The jar under home / JRE / lib / ext is loaded into memory or loaded by - DJava Ext.dir loads the class library in the specified location into memory. Developers can directly use the standard extension class loader. The specific loading path can be through system getProperty(“java.ext.dir”).

3. System loader

System loader: the system class loader is implemented by Sun's AppClassLoad. Like ExtClassLoad, it is also Sun misc. The static inner class of luncher. It is responsible for the user classpath (java -classpath) or - DJava class. The jar at the location specified by the path parameter is loaded into memory, that is, the path of the current class and its referenced third-party class library. Developers can directly use the system class loader and system Getproperty ("java.class.path") to view the loading path.

3, Loading mechanism of class loader

The Class loader is responsible for When the Class file is loaded into memory, the system generates Class objects for all classes loaded into memory. Once the Class is loaded, it will not be loaded for the second time. The identification to judge whether a Class is loaded is: Class fully qualified name + Class loader. For example, the Person Class under pg package is loaded into memory by CL1 Class loader, and the unique identification is (Person, pg, CL1), When CL2 loads the same Class into memory, the unique identifier is (Person, pg, CL2), and the following two classes are completely different and incompatible with each other. However, obviously, loading the same Class by two loaders is actually redundant. How can the loading mechanism of Class loader prevent this from happening?
There are three loading mechanisms of class loader, which jointly complete the loading of classes, making the loading of classes safe and efficient.

1. Overall responsibility

When a class loader is responsible for loading a class, all classes referenced by this class are loaded with this loader, unless it is shown that other class loaders are called, so as to avoid a class being loaded repeatedly.

2. Parental delegation

Parent delegation, also known as parent class delegation, means that when loading a class, the parent class loader attempts to load the class. If the parent class has a parent class, it recurses to the top-level parent class loader (the boot class loader has no parent class, it is the ancestor class loader). If the ancestor class loader cannot load the current class, it reversely delegates to the child class loader of the ancestor class, By analogy, until the source class loader, the parent-child relationship here is not inheritance, but a composite relationship, which is the reuse of the class loader.
Will The process of loading the class file into memory is completed by the parental delegation mechanism.
advantage:
        1. Avoid a class being loaded multiple times
        2. It is relatively secure and prevents tampering with the core API. The core API of Java is loaded by the boot class loader, and the parent delegation will always request the parent class loader to load. Generally, the boot class loader will be found to load the class first. Then, if a Java. Java is transmitted from the network util. The integer class needs to be loaded. At this time, the boot class loader is found. The boot class loader finds that this class has been loaded by me, so it will not load it and return directly
        3. Or, I just defined a java Lang. myinteger, and found the boot class loader. The boot class loader found the java core class library java Lang does not have this class at all, so it will delegate to subclasses in reverse, but java Lang needs access permission. Ordinary loaders cannot access loading. Forced loading will report an error.

3. Caching mechanism

When the jvm finishes loading a class, the class will be put into the jvm cache. The loading process is to go to the cache first to check whether the current class is loaded. If not, read it Class file and load it. If it is loaded, it will return directly

4, Custom class loader

1. Write a custom class loader
To implement a user-defined class loader, you need to inherit the ClassLoader class and override the findClass method. The code is as follows:

public class MyClassLoader extends ClassLoader {
	// The default read class path, you can define it arbitrarily
    private String path = "D:\\workspace-IDEA\\cloud2020\\spring-cloud-test\\target\\classes\\";
    private final String fileType = ".class"; // file type
    private String name; // File fully qualified name, e.g. com entity. Person
    // Name is the name of the class loader. Calling the parameterless constructor of the parent class in this constructor will initialize the class loader. By default, the system class loader will be used as the parent class loader
    // You can also specify the parent class loader with the constructor below You can look at the source code of the nonparametric constructor of the ClassLoader class
    public MyClassLoader(String name) {
        super();
        this.name = name;
    }
    // You can specify the construction method of the parent class loader
    public MyClassLoader(ClassLoader parent, String name) {
        super(parent);
        this.name = name;
    }
    // Find the class file you need to load and read the byte array in your own way. Finally, reuse the java api to get a class object and return it
    @Override
    protected Class<?> findClass(String name) {
        byte[] b = loadClassData(name);
        return defineClass(name, b, 0, b.length);
    }
    // Read class bytecode file to byte array
    private byte[] loadClassData(String name) {
        byte[] data=null;
        InputStream in=null;
        name=name.replace('.', '/');
        ByteArrayOutputStream out=new ByteArrayOutputStream();
        try {
            in=new FileInputStream(new File(path+name+fileType));
            int len=0;
            while(-1!=(len=in.read())){
                out.write(len);
            }
            data=out.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return data;
    }
	// Override the toString method to follow this getClass(). getClassLoader()
    @Override
    public String toString() {
        // TODO Auto-generated method stub
        return this.name;
    }
}

If you want to break the parental delegation model, you need to rewrite the loadClass method and the loadClass method of ClassLoader. You can look at the source code. I won't break the parental delegation model here.
2. Test
Loaded classes:

public class One{
   public One() {
       // Load the class loader of the current class in the console output
       System.out.println("One: i am loaded by " + this.getClass().getClassLoader());
   }
}
------------------
public class Two{
   public One() {
       // Load the class loader of the current class in the console output
       System.out.println("Two: i am loaded by " + this.getClass().getClassLoader());
   }
}

Main method:

 public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
    // Create your own root class loader. The default parent class loader is the java system class loader, and set the loading path
    MyClassLoader myClassLoader0 = new MyClassLoader("myRootClassLoader");
    myClassLoader0.setPath("/app/zero/");
    // Create your own extended class loader. The parent class is the custom root class loader, and set the loading path
    MyClassLoader myClassLoader1 = new MyClassLoader(myClassLoader0, "myExtendClassLoader");
    myClassLoader1.setPath("/app/one/");
	// Call the class file that does not exist in the custom extension class loader loader but exists in / app/zero /
    Class<?> aClass1 = myClassLoader1.loadClass("com.wangxs.springcloud.classloader.One");
    aClass1.newInstance(); // The output should be myRootClassLoader
}

In the above code, the parent class of the custom root class loader is the java system class loader, which is now one The class file exists in the / app/zero / directory, but not in the program class path. I use the custom extended class loader to load one Class, according to the parent delegation mechanism, we can infer that it will delegate the java system class loader upward to load, but the system class loader finds that it cannot load this class, and instead delegates it to the custom root class loader in reverse. The output result should be: myRootClassLoader

public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
	// Create your own system class loader without parent class loader and set the loading path
    MyClassLoader myClassLoader2 = new MyClassLoader(null, "myAppClassLoader");
    myClassLoader2.setPath("/app/two/");
	// class files that do not exist in the loader but exist in / app/two /
    Class<?> aClass2 = myClassLoader2.loadClass("com.wangxs.springcloud.classloader.Two");
    aClass2.newInstance(); // The output should be myAppClassLoader
}

The above code creates a custom system class loader. If there is no parent class loader, the class is loaded directly by the loader, and the result should be myAppClassLoader

The above results are shown in the figure below:

The above is a summary of my knowledge about JVM class loader. The previous article also introduced the class loading process and garbage collection mechanism. You can have a look.

Keywords: Java JDK jvm

Added by stevenszabo on Sun, 23 Jan 2022 15:28:43 +0200