In the previous chapter, we looked at the Class loading process. We learned that during the loading phase, we need to “get the binary stream describing a Class by its fully qualified name”. The Class Loader does this.

Classes and classloaders

Class loaders are only used to implement the loading action of classes.

But for any class, the uniqueness of the Java virtual machine must be established by the class loader that loads it and the class itself. Each class loader has a separate class namespace.

This sentence can express more popular: compare whether the two classes “equal”, only in these two classes are from the same Class loader under the premise of load to be meaningful, otherwise, even if these two classes derived from the same Class files, by the same Java virtual machine loading, as long as the load they’re different Class loaders, these two classes are not necessarily equal.

The following illustrates how different classloaders affect the results of the instanceof keyword operation.

public class ClassLoaderTest {
    public static void main(String[] args) throws Exception {
        // Create a simple class loader
        ClassLoader myLoader = new ClassLoader() {
            @Override
            // Load the class method
            publicClass<? > loadClass(String name)throws ClassNotFoundException {
                try {
                    // Get the file name
                    String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
                    // Load the input stream
                    InputStream is = getClass().getResourceAsStream(fileName);
                    // Use parent class loading
                    if (is == null) {
                        return super.loadClass(name);
                    }
                    byte[] b = new byte[is.available()];
                    is.read(b);
                    // Convert an instance of the class from the stream
                    return defineClass(name, b, 0, b.length);
                } catch (IOException e) {
                    throw newClassNotFoundException(name); }}};// Use your own class loader to load
        Object obj = myLoader.loadClass("cn.fighter3.loader.ClassLoaderTest").newInstance();
        System.out.println(obj.getClass());
        // Instance judgment
        System.out.println(obj instanceofcn.fighter3.loader.ClassLoaderTest); }}Copy the code

Running results:

In the code defines a simple class loader, use this class loader for the cn. Fighter3. Loader. ClassLoaderTest class and create an instance, to do the type checking, found that the result is false.

2. Parental delegation model

From the perspective of the Java virtual machine, there are only two different class loaders: the Bootstrap ClassLoader, which is implemented in C++ and is part of the virtual machine itself; The other is all the other classloaders, which are implemented in the Java language, exist independently of the virtual machine, and all inherit from the abstract java.lang.classloader class.

From a Java developer’s point of view, class loaders should be more nuanced. Since JDK 1.2, Java has maintained a three-tier, parent-delegated class loading architecture.

The parental delegation model is shown in the figure above:

  • Bootstrap Class Loader: Is responsible for loading files that are stored in

    \lib, or in the path specified by the -xbootclasspath parameter, and can be recognized by the Java virtual machine (by file name, such as rt.jar, tools.jar, Libraries with incorrect names will not be loaded even if placed in the lib directory.
  • The Extension Class Loader is responsible for loading all libraries in the

    \lib\ext directory, or in the path specified by the java.ext.dirs system variable.
  • The Application Class Loader is responsible for loading all libraries on the user’s ClassPath. If there is no custom Class Loader, this Loader is generally the default Class Loader in the program.

Users can also add custom class loaders to extend.

The working process of the parental delegation model: If a class loader received the request of the class loading, it won’t try to load the first class, but to delegate the request to the parent class loader to complete, each level of class loaders, so all the load request should be transferred to the top finally start the class loader, only when the parent feedback they can’t finish the load request, The child loader will try to complete the load itself.

Why parent delegation?

The answer is to keep the application stable and orderly.

For example, the java.lang.Object class, which is stored in rt.jar, is guaranteed to be loaded by the parent delegate mechanism to the starting class loader at the top of the model, ensuring that the Object is consistent. If the user writes a class named java.lang.Object and places it in the program’s ClassPath, the system will have multiple Object classes.

Parents delegation model code is very simple, in Java. Lang. This. One of the Java loadClass method:

    protectedClass<? > loadClass(String name,boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check whether the class has been loadedClass<? > c = findLoadedClass(name);if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if(parent ! =null) {
                        c = parent.loadClass(name, false);
                    } else{ c = findBootstrapClassOrNull(name); }}catch (ClassNotFoundException e) {
                    // If the parent class loader throws a ClassNotFoundException
                    // Indicates that the parent class loader cannot complete the load request
                }

                if (c == null) {
                    // When the parent class loader cannot be loaded
                    // Call its own findClass method for class loading
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); }}if (resolve) {
                resolveClass(c);
            }
            returnc; }}Copy the code

3. Break the parental delegation model

There are three major breaks in parental delegation:

First failure

The first “break” of the parental delegation model actually occurred before the advent of the parental delegation model — in the “ancient” days before JDK 1.2 came out.

Since the parent delegate model was introduced after JDK 1.2, the concept of classloaders and the abstract java.lang.classloader existed in the first version of Java, and there is no technical way to avoid the possibility of loadClass() being overridden by subclasses in order to be backward compatible with older code. You can only add a new protected method, findClass(), to java.lang.classloader after JDK 1.2 and override it as much as possible when instructing user-written classloading logic instead of writing code in loadClass().

Secondary failure

The second “break” of the parent delegate model is caused by a flaw in the model itself. What if the underlying type calls back to the user’s code?

For example, we are familiar with JDBC:

Java defines the corresponding SPI in the core package \lib, so this is no doubt loaded by the boot class loader loader.

The implementation of each vendor, however, cannot be placed in the core package, only in the classpath, only loaded by the application class loader. The problem, then, is that starting the class loader does not load the SPI service code provided by the vendor.

To solve this problem, we introduced a less elegant design: the Thread Context ClassLoader. This ClassLoader can be set using the setContext-classloader () method of the java.lang.Thread class. If it has not been set when the Thread is created, it will inherit one from the parent Thread, if it has not been set at the global level of the application. This class loader is the application class loader by default.

The JNDI service uses this thread-context class loader to load the required SPI service code, which is the behavior of the parent class loader to request the subclass loader to complete the class loading.

Third failure

The third “break” of the parental delegation model is due to users’ pursuit of program dynamics, such as Hot Swap, Hot Deployment of modules, etc.

Each program module (called a Bundle in OSGi) has its own class loader. When a Bundle needs to be replaced, the Bundle is replaced with the same kind of loader to achieve the hot replacement of code. In the OSGi environment, class loaders move away from the tree structure recommended by the parent delegate model and evolve into a more complex network structure.


“Do simple things repeatedly, do repetitive things carefully, and do serious things creatively!” –

I am three points evil, can call me old three/three minutes/three elder brother/three son, a full stack of literary and military development, let’s see next period!




Reference:

[1] : Understanding the Java Virtual Machine in Depth: Advanced JVM Features and Best Practices (3rd edition)

[4] : There are three times in Java history that the parental delegation model has been broken. Which three times?