This article takes about 9 minutes to read.

In the last article, we reviewed the basic concepts of class loaders: the life cycle of a class, what class loaders do, when classes load and unload, and so on. In this article, we continue to review class loaders, including: What class loaders are available in the JVM? What is the relationship between them? What is the parent delegate mechanism?

Parental delegation model

From a Java developer’s perspective, you need to understand the parent delegate model of the classloader, as shown in the following figure:

  • Bootstrap ClassLoader: starts a ClassLoader that loads into memory jar libraries that are stored in the /lib directory, specified by the -xbootclasspath parameter, and recognized by the virtual machine. To put it more plainly, the classes that start with java.lang must be loaded by Bootstrap ClassLoader.

  • The Extension this: The extension classloader, implemented by sun.misc.Launcher$ExtClassLoader, is responsible for loading all libraries in the /lib/ext directory, or in the path specified by the java.ext.dirs system variable.

  • Application ClassLoader: An Application ClassLoader implemented by sun.misc.Launcher$AppClassLoader that loads all libraries in the path specified by the user CLASSPATH environment variable. This is the default class loader for Java applications that do not have their own custom class loader.

  • User-defined class loaders: Users can implement their own custom class loaders if necessary. Generally speaking, custom class loaders are required in the following situations: (1) Isolation of loading classes. Some frameworks require middleware and application to use different class loaders in order to achieve the isolation of middleware and application modules. (2) Modify the method of class loading. The parent-delegate model of class loading is not mandatory and users can load classes dynamically at a certain point in time as needed. (3) Extend the class loading source, such as from the database, network class loading; (4) Prevent source code leakage. Java code can be easily decompiled and tampered with. To prevent source leaks, you can encrypt your class’s bytecode files and write custom classloaders to load your own application’s classes.

In the following code, java.util.HashMap is a class in the rt.jar package, so its classloader is null, and the DNSNameService class is a class in the JAR package in the ext directory, so its classloader is ExtClassLoader; The class loader for MyClassLoaderTest is the application class loader.

The access code running above is shown below:

As you can see from the following program, each class loader is responsible for a different JAR file path:

    The above code runs as follows:

    Arthas provides the classloader command to view statistics about classloaders in your current application, as shown in the following figure.

    1. The table displayed after entering classloader summarizes the currently applied classloaders, the number of instances of each classloader, and the number of classes loaded by each classloader.

    2. BootStrap classloader is at the top of the classloader system, followed by the extension classloader, and then the application classloader. There is also ArthasClassLoader, which is a custom class loader implemented by Arthas itself.

    Parents delegate the working process of the model

    If a classloader receives a classload request, it does not try to load the class itself at first. Instead, it delegates the request to the parent classloader. This is true at every level of classloaders, so all load requests should eventually be passed to the top level of the starting classloader. Only when the parent loader reports that it cannot complete the load request (it did not find the desired class in its search scope) will the child loader attempt to load it itself.

    One of the obvious benefits of using the parent-delegate model to organize relationships between class loaders is that Java classes have a hierarchical relationship with priority along with their classloaders. For example, the java.lang.Object class, which is stored in rt.jar, is ultimately delegated to the boot class loader at the top of the model by any classloader. Therefore, the Object class is the same class in the various classloader environments of the program. Instead of using the parent delegate model, which is loaded by each Class loader, if the user writes a Class called java.lang.Object and places it in the program’s Class Path, there will be multiple different Object classes. The most basic behavior in the Java type system is not guaranteed, and the application becomes a mess.

    The implementation of the parent delegation model is very simple. The code to implement the parent delegation is in the loadClass() method of java.lang.classLoader, as shown below:

      Break the parent delegate model

      As mentioned above, the parent delegate model nicely addresses the problem of unifying the base classes of each class loader (the more basic classes are loaded by the higher loader). What if the base class calls back to the user’s class? A classic example is the SQL driver management class java.sql.DriverManager.

      Java.sql.DriverManager is a standard Java service. The class is stored in rt.jar, so it is loaded by the startup class loader. However, when the application is started, the driver class manager needs to load drivers implemented by different database vendors, but the startup class loader cannot find these specific implementation classes. To solve this problem, the Java design team provided a less elegant design: Thread Context ClassLoader.

      This classloader can be set using the setContextClassLoader() method of the java.lang.Thread class. If it has not been set when the Thread is created, it inherits one from the parent Thread. If it has not been set at the global scope of the application, The class loader is the application class loader.

      With a thread context loader, you can solve this problem — the parent class loader needs to request the subclass loader to complete the classloading action, which in effect breaks the parent delegate’s loading rules.

      Next, we take a look at the use of the thread context loader using java.sql.DriverManager as an example. The static block below the java.SQL.DriverManager class is the JDBC driver loading entry.

        Following the loadInitialDrivers() method, the place to use the thread context loader is in Serviceloader.load

        The code for the Serviceloader.load method is as follows, and JDBC’s sqlDriverManager is the context loader obtained here to drive the user code to load the specified class.

        So when was the context loader set up? We mentioned earlier:

        The class loader can be set using the setContextClassLoader() method of the java.lang.Thread class. If it has not been set when the Thread is created, it inherits one from the parent Thread, if the global scope of the application is not set. The class loader is the application class loader.

        Take a look at the setContextClassLoader() method and see who called it. Finally, we find the following code in the Launcher:

          conclusion

          In this article we reviewed the parent-delegate model of class loaders, how it works, and the need to break the parent-delegate model and source code analysis. At the end of the first part, we also demonstrated the use of Arthas commands for classloaders that you might want to consider when troubleshooting actual problems.

          The resources

          1. Java ClassLoader

          2. https://www.cnblogs.com/joemsu/p/9310226.html

          3. In-depth Understanding of the Java Virtual Machine

          4. Coding Efficient Java Development Manual

          Phase to select


          JVM parameter best practices: The initial and maximum size of the meta-space

          Understand JVM class loaders once and for all: Basic concepts

          Why do Java processes use more RAM than Heap Size?

          MySQL timestamp accuracy caused by blood murder

          This issue focuses on topics such as backend technology, JVM troubleshooting and optimization, Java interview questions, personal growth and self-management, providing readers with front-line developer work and growth experience, looking forward to your harvest here.