JVM_21_ On class loader

Posted by creet0n on Thu, 24 Feb 2022 14:51:59 +0100

1. General

Class loader is the premise for JVM to execute class loading mechanism.

Functions of ClassLoader:
ClassLoader is the core component of Java. All classes are loaded by ClassLoader. ClassLoader is responsible for reading the binary data stream of Class information into the JVM through various ways and converting it into a Java corresponding to the target Class Lang. Class object instance. Then give it to the Java virtual machine for linking, initialization and other operations. Therefore, in the whole loading stage, ClassLoader can only affect the loading of classes, but cannot change the link and initialization behavior of classes through ClassLoader. Whether it can run or not is decided by the Execution Engine.

1.1 class loading classification (explicit loading VS implicit loading)


give an example:

public class UserTest {
    public static void main(String[] args) {

        try {
            // Implicit loading
            User user = new User();
            // Load explicitly and initialize
            Class clazz = Class.forName("com.test.java.User");
            // Load explicitly, but not initialize
            ClassLoader.getSystemClassLoader().loadClass("com.test.java.Parent");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

Necessity of class 1.2 loader

1.3 namespace

// Custom class loader
public class UserClassLoader extends ClassLoader {
    private String rootDir;

    public UserClassLoader(String rootDir) {
        this.rootDir = rootDir;
    }

    /**
     * Write the logic of the findClass method
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // Gets the byte array of the class file of the class
        byte[] classData = getClassData(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        } else {
            //Generate class objects directly
            return defineClass(name, classData, 0, classData.length);
        }
    }

    /**
     * Write the logic to get the class file and convert it into byte code stream * @ param className * @return
     */
    private byte[] getClassData(String className) {
        // Read bytes of class file
        String path = classNameToPath(className);
        try {
            InputStream ins = new FileInputStream(path);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            // Read bytecode of class file
            while ((len = ins.read(buffer)) != -1) {
                baos.write(buffer, 0, len);
            }
            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Full path to class file
     */
    private String classNameToPath(String className) {
        return rootDir + "/" + className.replace('.', '/') + ".class";
    }


    // test
    public static void main(String[] args) {
        String rootDir = "/Users/zhangshouan/WorkSpace/spring-kernel/jvm/src";

        try {
            // Create custom class loader 1
            UserClassLoader loader1 = new UserClassLoader(rootDir);
            Class clazz1 = loader1.findClass("com.jvm.lecture.test.User");

            // Create a custom class loader 2
            UserClassLoader loader2 = new UserClassLoader(rootDir);
            Class clazz2 = loader2.findClass("com.jvm.lecture.test.User");
            // clazz1 and clazz2 correspond to different class template structures
            System.out.println(clazz1 == clazz2);  // false clazz1 and clazz2 correspond to different class template structures
            System.out.println(clazz1.getClassLoader()); //  com.jvm.lecture.test.UserClassLoader@28d93b30
            System.out.println(clazz2.getClassLoader()); //  com.jvm.lecture.test.UserClassLoader@4554617c

            Class clazz3 = ClassLoader.getSystemClassLoader().loadClass("com.jvm.lecture.test.User");
            System.out.println(clazz3.getClassLoader()); //  sun.misc.Launcher$AppClassLoader@18b4aac2
            System.out.println(clazz1.getClassLoader().getParent()); //  sun.misc.Launcher$AppClassLoader@18b4aac2

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

1.4 basic characteristics of class loading mechanism

2. Class loader classification

The JVM supports two types of class loaders: Bootstrap ClassLoader and User-Defined ClassLoader.

Conceptually speaking, custom class loader generally refers to a class loader customized by developers in the program, but the Java virtual machine specification does not define it. Instead, all class loaders derived from the abstract class ClassLoader are divided into custom class loaders. No matter how the types of class loaders are divided, the most common class loader structures in the program are as follows:

  • Except for the top-level startup class loader, all other class loaders should have their own "parent class" loader.
  • Different class loaders seem to be Inheritance relationships, but they are actually inclusion relationships. The lower loader contains a reference to the upper loader.
class ClassLoader{
    ClassLoader parent;//Parent class loader
        public ClassLoader(ClassLoader parent){
        this.parent = parent;
    }
}
class ParentClassLoader extends ClassLoader{
    public ParentClassLoader(ClassLoader parent){
        super(parent);
    }
}
class ChildClassLoader extends ClassLoader{
    public ChildClassLoader(ClassLoader parent){ //parent = new ParentClassLoader();
        super(parent);
    }
}

2.1 Bootstrap ClassLoader

Boot classloader (boot classloader, Bootstrap ClassLoader)

  • This class loading is implemented in C/C + + language and nested inside the JVM.
  • It is used to load the core library of Java (the contents in JAVAHOME/jre/lib/rt.jar or sun.boot.class.path path path). Used to provide classes required by the JVM itself.
  • Does not inherit from Java Lang. classloader, no parent loader.
  • For security reasons, the Bootstrap startup class loader only loads classes with package names beginning with java, javax, sun, etc
  • Load extension classes and application class loaders and specify them as their parent class loaders.
  • Use the - XX:+TraceClassLoading parameter to get.



The startup class loader is written in C + +? Yes!

  • C/C + +: pointer function & function pointer, C + + supports multiple inheritance and is more efficient
  • Java: evolved from C + + (c + +) -- version, single inheritance

2.2 Extension ClassLoader

public class ClassLoaderTest1 {
    public static void main(String[] args) {

        System.out.println("**********Start class loader**************");
        // Get the path of the api that bootstrap classloader can load
        URL[] urLs = sun.misc.Launcher.getBootstrapClassPath().getURLs();
        for (URL element : urLs) {
            System.out.println(element.toExternalForm());
        }
        // Select a class randomly from the above path to see what its classloader is: boot classloader
        ClassLoader classLoader = Provider.class.getClassLoader();
        System.out.println(classLoader); // null proves again that we cannot get the boot class loader

        System.out.println("***********extensions class loader *************");
        String extDirs = System.getProperty("java.ext.dirs");
        for (String path : extDirs.split(";")) {
            System.out.println(path);
        }

        // Select a class randomly from the above path to see what its classloader is: extended classloader
        ClassLoader classLoader1 = CurveDB.class.getClassLoader();
        System.out.println(classLoader1);//sun.misc.Launcher$ExtClassLoader@1540e19d

    }
}

**********Start class loader**************
file:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/resources.jar
file:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/rt.jar
file:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/sunrsasign.jar
file:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/jsse.jar
file:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/jce.jar
file:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/charsets.jar
file:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/jfr.jar
file:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/classes
null
***********extensions class loader *************
/Users/zhangshouan/Library/Java/Extensions:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/ext:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java
sun.misc.Launcher$ExtClassLoader@1540e19d

2.3 system class loader (AppClassLoader)

public class ClassLoaderTest {
    public static void main(String[] args) {

        // Get system class loader
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader); // sun.misc.Launcher$AppClassLoader@18b4aac2

        // Get its upper layer: extension class loader
        ClassLoader extClassLoader = systemClassLoader.getParent();
        System.out.println(extClassLoader); // sun.misc.Launcher$ExtClassLoader@28d93b30

        // Get its upper layer: cannot get boot class loader
        ClassLoader bootstrapClassLoader = extClassLoader.getParent();
        System.out.println(bootstrapClassLoader); // null

        // For user-defined classes, the system class loader is used by default
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        System.out.println(classLoader); // sun.misc.Launcher$AppClassLoader@18b4aac2

        // The String class is loaded using the boot class loader. -- > Java's core class libraries are loaded using the bootstrap class loader.
        ClassLoader classLoader1 = String.class.getClassLoader();
        System.out.println(classLoader1); // null

    }
}

2.4 user defined class loader


Test different class loaders

public class ClassLoaderTest2 {
    public static void main(String[] args) {
        try {

            // 1.Class.forName().getClassLoader()
            ClassLoader classLoader = Class.forName("java.lang.String").getClassLoader();
            System.out.println(classLoader); // The null String class is loaded by the startup class loader and we cannot get it

            // 2.Thread.currentThread().getContextClassLoader()
            // Gets the ClassLoader of the current thread context
            ClassLoader classLoader1 = Thread.currentThread().getContextClassLoader();
            System.out.println(classLoader1); //sun.misc.Launcher$AppClassLoader@18b4aac2

            // 3.ClassLoader.getSystemClassLoader().getParent()
            ClassLoader classLoader2 = ClassLoader.getSystemClassLoader().getParent();
            System.out.println(classLoader2); //sun.misc.Launcher$ExtClassLoader@61bbe9ba

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

// Gets the ClassLoader of the current class
clazz.getClassLoader()
// Gets the ClassLoader of the current thread context
Thread.currentThread().getContextClassLoader()
// Get the ClassLoader of the system
ClassLoader.getSystemClassLoader()

explain
From the perspective of program, the boot class loader and the other two kinds of loaders (system class loader and extension class loader) are not loaders in the same level. The boot class loader is written in C + + language, while the other two kinds of loaders are written in Java language. Since the boot class loader is not a Java class at all, it can only print null values in Java programs.

The Class object of the array Class is not created by the Class loader, but automatically created by the JVM as needed during the Java runtime. For the Class loader of array Class, it is through Class The returned by getclassloader () is the same as the Class loader of element type in the array; If the element type in the array is the basic data type, the array Class has no Class loader.

// Run result: null
String[] strArr = new String[6];
System.out.println(strArr.getClass().getClassLoader());

// Running result: sun.misc.Launcher $AppCLassLoader @ 18b4aac2
// Same as the loader of the class of array elements
ClassLoaderTest[] test=new ClassLoaderTest[1];
System.out.println(test.getClass().getClassLoader());

// Run result: null, no class loader required
int[]ints =new int[2];
System.out.println(ints.getClass().getClassLoader());

3.ClassLoader source code analysis

Relationship between ClassLoader and existing classes:

In addition to the loader of the above virtual machine, users can also customize their own class loader. Java provides abstract classes Lang. ClassLoader. All user-defined class loaders should inherit the ClassLoader class.

3.1 source code analysis of extclassloader and AppClassLoader

Launcher

First, create an extension class loader



Parent class is NULL (upper class loader)


The extension class loader instance is passed into the system class loader


The parent class of the system class loader is the extension class loader

The ClassLoader that gets the current thread context is AppClassLoader

3.2 main methods of classloader



Simple example

protected Class<?> findClass(String name) throws ClassNotFoundException {
    // Gets the byte array of the class
    byte[] classData =getClassData(name);
    if (classData == null) {
        throw new ClassNotFoundException();
    } else{
        //Generate class objects using defineClass
        return defineClass(name,classData,θ,classData.length);
    }
}

1. public final ClassLoader getParent()

Gets the front edge setting of the parent

2. public Class<?> loadClass(String name) throws ClassNotFoundException


Analysis of loadClass() (logic of parent delegation mechanism)
Test code: classloader getSystemClassLoader(). loadClass(“cn.jvm.java.User”);

// Resolve: when true, the class will be parsed at the same time when it is loaded
// resolve:false no resolution required
protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    // The synchronization operation can only be loaded once
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        // First, judge whether the class with the same name has been loaded in the cache
        Class<?> c = findLoadedClass(name);
        // without
        if (c == null) {
            // Gets the current system time
            long t0 = System.nanoTime();
            try {
                // Gets the parent class loader of the current class loader
                if (parent != null) {
                    // If there is a parent class loader, call the parent class loader to load the class
                    c = parent.loadClass(name, false);
                } else {
                    // The parent is null, and the parent class loader is the boot class loader
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            // The parent class loader of the loader of the current class does not load this class or the current class loader does not load this class
            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                long t1 = System.nanoTime();
                // Call the findClass() method of the current ClassLoader
                // findClass()--> defineClass()
                c = findClass(name); // Real loading method

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        // Do you want to parse
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

3.3 SecureClassLoader and URLClassLoader


3.4 ExtClassLoader and AppClassLoader



We found that ExtClassLoader did not override the loadClass() method, which is enough to show that it follows the parental delegation mode. AppClassLoader overloads the loadClass() method, but ultimately calls the parent loadClass() method, so it still follows the parental delegation mode.

3.5 Class.forName() and classloader loadClass()

4. Parental delegation model

The class loader is used to load classes into the Java virtual machine. From jdk1 Since version 2, the class loading process adopts the parental delegation mechanism, which can better ensure the security of the Java platform.

4.1 definition and essence

definition
If a class loader receives a request to load a class, it will not try to load the class itself, but delegate the request task to the parent class loader to complete it, recursion in turn. If the parent class loader can complete the class loading task, it will return successfully. Only when the parent class loader cannot complete this loading task can it load it by itself.

essence
It specifies the order of class loading: the boot class loader loads first. If it cannot be loaded, it will be loaded by the extension class loader. If it cannot be loaded, it will be loaded by the system class loader or the user-defined class loader.


4.2 advantages and disadvantages



4.3 destroy the parental appointment mechanism

The parental delegation model is not a model with mandatory constraints, but a class loader implementation recommended by Java designers to developers.

In the Java world, most class loaders follow this model, but there are exceptions. Until the emergence of Java modularization, the parental delegation model has been "broken" on a large scale for three times.

Breaking the parental delegation mechanism for the first time

The second time to destroy the parental delegation mechanism: thread context class loader



The default context loader is the application class loader. In this way, the loader above and below acts as an intermediary, so that the code in the startup class loader can also access the classes in the application class loader.

Destroy the parental delegation mechanism for the third time

4.4 realization of hot replacement


5. Sandbox security mechanism


JDK1.0 period
In Java, executable programs are divided into local code and remote code. Local code is regarded as trusted by default, while remote code is regarded as untrusted. For the local code of credit granting, you can access all local resources. For non credit remote code, in the early Java implementation, security depended on the Sandbox mechanism. As shown in the figure below, jdk1 0 security model

JDK1.1 period
JDK1. The strict security mechanism in 0 also brings obstacles to the function expansion of the program. For example, when users want remote code to access the files of the local system, it cannot be realized.

Therefore, in the subsequent Java 1 Security mechanism is improved for version 1. Allows users to specify code access to local resources.

As shown in the figure below, jdk1 1 security model

JDK1.2 period
In Java 1 In version 2, the security mechanism is improved again and the code signature is added. Whether local code or remote code, it will be loaded into the running space with different permissions in the virtual machine by the class loader according to the user's security policy settings to realize differentiated code execution permission control. As shown in the figure below, jdk1 2. Security model:

JDK1.6 period

6. Loader of custom class


be careful
In general, using different class loaders to load different functional modules will improve the security of applications. However, if Java type conversion is involved, the loader is prone to bad things. During Java type conversion, type conversion can only be carried out when both types are loaded by the same loader, otherwise exceptions will occur during conversion.

6.1 implementation mode


explain
Its parent class loader is the system class loader.
Java. Java is used for all class loads in the JVM lang.ClassLoader. Loadclass (string) interface (except for customizing the class loader and rewriting the java.lang.ClassLoader.loadClass(String) interface), even the core class library of JDK can not be an exception.

// Custom class loader
public class UserClassLoader extends ClassLoader {
    private String rootDir;

    public UserClassLoader(String rootDir) {
        this.rootDir = rootDir;
    }

    /**
     * Write the logic of the findClass method
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // Gets the byte array of the class file of the class
        byte[] classData = getClassData(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        } else {
            //Generate class objects directly
            return defineClass(name, classData, 0, classData.length);
        }
    }

    /**
     * Write the logic to get the class file and convert it into byte code stream * @ param className * @return
     */
    private byte[] getClassData(String className) {
        // Read bytes of class file
        String path = classNameToPath(className);
        try {
            InputStream ins = new FileInputStream(path);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            // Read bytecode of class file
            while ((len = ins.read(buffer)) != -1) {
                baos.write(buffer, 0, len);
            }
            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Full path to class file
     */
    private String classNameToPath(String className) {
        return rootDir + "/" + className.replace('.', '/') + ".class";
    }


    // test
    public static void main(String[] args) {
        String rootDir = "/Users/zhangshouan/WorkSpace/spring-kernel/jvm/src";

        try {
            // Create custom class loader 1
            UserClassLoader loader1 = new UserClassLoader(rootDir);
            Class clazz1 = loader1.findClass("com.jvm.lecture.test.User");

            // Create a custom class loader 2
            UserClassLoader loader2 = new UserClassLoader(rootDir);
            Class clazz2 = loader2.findClass("com.jvm.lecture.test.User");
            // clazz1 and clazz2 correspond to different class template structures
            System.out.println(clazz1 == clazz2);  // false clazz1 and clazz2 correspond to different class template structures
            System.out.println(clazz1.getClassLoader()); //  com.jvm.lecture.test.UserClassLoader@28d93b30
            System.out.println(clazz2.getClassLoader()); //  com.jvm.lecture.test.UserClassLoader@4554617c

            Class clazz3 = ClassLoader.getSystemClassLoader().loadClass("com.jvm.lecture.test.User");
            System.out.println(clazz3.getClassLoader()); //  sun.misc.Launcher$AppClassLoader@18b4aac2
            System.out.println(clazz1.getClassLoader().getParent()); //  sun.misc.Launcher$AppClassLoader@18b4aac2

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

7. New features of Java 9


2. Platform class loader and application class loader are no longer inherited from Java net. URLClassLoader.
Now the startup class loader, platform class loader and application class loader all inherit from JDK internal. loader. BuiltinClassLoader.


If a program directly relies on this inheritance relationship or a specific method of the URLClassLoader class, the code is likely to crash in JDK 9 and later.

3. In Java 9, the class loader has a name. The name is specified in the construction method and can be obtained through the getName() method. The name of the platform class loader is platform, and the name of the application class loader is app. The name of the classloader can be useful when debugging problems related to the classloader.

4. The boot class loader is now a class loader implemented in collaboration with the java class library within the jvm (formerly implemented in C + +), but in order to be compatible with the previous code, null will still be returned in the scenario of obtaining the boot class loader instead of the BootClassLoader instance.

5. The delegation relationship of class loading has also changed. When the platform and application class loader receives a class loading request, before delegating to the parent loader for loading, it is necessary to judge whether the class can belong to a system module. If such a belonging relationship can be found, it is necessary to give priority to delegating to the loader responsible for that module to complete the loading.



public class ClassLoaderTest {
    public static void main(String[] args) {
        System.out.println(ClassLoaderTest.class.getClassLoader());
        System.out.println(ClassLoaderTest.class.getClassLoader().getParent());
        System.out.println(ClassLoaderTest.class.getClassLoader().getParent().getParent());

        //Get system class loader
        System.out.println(ClassLoader.getSystemClassLoader());
        //Get platform class loader
        System.out.println(ClassLoader.getPlatformClassLoader());
        //Gets the name of the loader for the class
        System.out.println(ClassLoaderTest.class.getClassLoader().getName());
    }
}

Topics: Java jvm