Deep Understanding of Java Virtual Machine Chapter 7 Virtual Machine Class Loader

Keywords: Java

Class 7.4 Loader

In the class loading stage, the action of obtaining a binary byte stream describing such a class by a fully qualified name is implemented outside the Java virtual machine so that the application can decide how to get the required class.
The code module that implements this action becomes the class loader.

1. Classes and class loaders

- For any class, it is up to its class loader and the class itself to establish its uniqueness in the Java virtual machine. Each class loader has a separate class namespace.
That is to say, it is meaningful to compare whether two classes are equal only if they are loaded by the same class loader. Otherwise, even if the two classes originate from the same Class file and are loaded by the same virtual machine, they will inevitably be different as long as their class loaders are different.

  • Equality here, including
    1. The results returned by the equals(), isAssignableFrom(), isInstance() methods of the Class object representing the class
    2. Use instanceof keywords to determine the ownership of objects, etc.
public class ClassLoaderTest {

    public static void main(String[] args) throws Exception {

        ClassLoader myLoader = new ClassLoader() {
            @Override
            public Class<?> loadClass(String name) throws ClassNotFoundException {
                try {
                    String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
                    InputStream is = getClass().getResourceAsStream(fileName);

                    if (is == null) {
                        return super.loadClass(name);
                    }

                    byte[] b;
                    b = new byte[is.available()];
                    is.read(b);

                    return defineClass(name, b, 0, b.length);

                } catch (IOException e) {
                    throw new ClassNotFoundException(name);
                }
            }
        };

        Object obj = myLoader.loadClass("com.baiiu.test.ClassLoaderTest").newInstance(); //Current class object
        System.out.println(obj.getClass());
        System.out.println(obj instanceof ClassLoaderTest); //false
    }
}

2. Class loading classification

  • From a Java virtual machine perspective, there are two different loaders:
    One is Bootstrap Class Loader, implemented in C++, which is part of the virtual machine itself.
    The other is all other class loaders, implemented in the Java language, independent of the external virtual machine and all inherited from the abstract class java.lang.ClassLoader.

  • From a Java developer's point of view, most Java programs use classloaders provided by the following three systems.

    1. Bootstrap Class Loader (Bootstrap Class Loader, Boot Class Loader)
      This class loader is responsible for loading the class libraries (*. jar) identified by the virtual machine into the memory of the virtual machine stored in the <JAVA_HOME>\ lib directory, or in the path specified by the - Xbootclasspath parameter.
      The boot class loader can not be directly referenced by Java programs. When a user writes a custom loader, if he needs to delegate the load request to the boot class loader, he can directly use null instead.

    2. Extension Class Loader
      It is responsible for loading all class libraries in the lib ext directory or in the path specified by the java.ext.dirs system variable.
      Developers can use the Extended Class Loader directly.

    3. Application Class Loader
      This class loader is returned by ClassLoader#getSystemClassLoader(), so it is also called a system class loader. It is responsible for loading the class libraries specified on the user's classpath.
      Developers can use this class loader directly.
      If there is no custom class loader in the application, this is the default class loader in the program.

3. Parent Delegation Model

public class Test {
    public static void main(String[] args) {
        ClassLoader classLoader = Test.class.getClassLoader();
        while (classLoader != null) {
            System.out.println(classLoader);
            classLoader = classLoader.getParent();
        }
    }
}

sun.misc.Launcher$AppClassLoader@659e0bfd //system class loader
sun.misc.Launcher$ExtClassLoader@6d06d69c //extensions class loader
parent == null time findBootstrapClassOrNull(); //Start Class Loader
  • The Parent Delegation Model requires that all class loaders except the top-level boot class loaders should have their own parent class loaders.
    In general, the parent-child relationship between loaders here is not implemented by the Inheritance relationship, but by using the Composition relationship to reuse the code of the parent loader. (Combination mode)

  • The working process of the Parent Delegation Model:
    If a class loader receives a class loading request, it will not attempt to load the class itself first, but delegate the request to the parent class loader to complete, as is the case at every level of class loader.
    Because all load requests will eventually be sent to the top-level boot loader, the child loader will try to load them on its own only if the parent loader can't complete the request.

  • Benefits of the Parent Delegation Model:
    Java, along with its class loaders, has a hierarchical relationship with priority.
    For example, java.lang.Object, regardless of which class loader loads this class, is ultimately delegated to the top-most boot class loader, so the Object class is the same class in all classloader environments of the program.

  • The implementation code of the Parent Delegation Model:

    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException{

        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            // First, check that the requested class has been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        // If not defined, that is Application ClassLoader, returned by ClassLoader# getSystem ClassLoader ()
                        c = parent.loadClass(name, false);
                    } else {
                        // Start Class Loader Loading
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                    // The parent loader did not find this class and left it to the child loader to load itself.
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name); // When you do not define your own loader, you throw the exception ClassNotFound directly, which is usually found by the system class loader.

                    // 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();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

Posted by zkoneffko on Sat, 08 Jun 2019 12:57:02 -0700