An overview of the

This article mainly describes the JVM class loading process and the JVM provided by the centralized class loader and the parent delegation mechanism, through the Tomcat class loading mechanism to explain how to break the parent delegation mechanism.

Class loading process

The timing of class loading

When a type is loaded into vm memory, its entire life cycle goes through seven stages: load, validate, prepare, parse, initialize, use, and unload. Verify, prepare, and parse are connected.

7 cases in which a class is actively loaded

  1. Create an instance of the class
  2. Accesses or assigns a value to a static variable of a class or interface
  3. Call a static method of a class
  4. Reflection (e.g. Class.forname (” com.test.test “)
  5. Initialize a subclass of a class
  6. The class marked as the startup class when the Java virtual machine starts is the class that contains the main method (Java Test).
  7. JDK1.7 began to provide dynamic language support, Java. Lang. Invoke. Analytical results REF_getStatic MethodHandle instance, REF_putStatic, REF_invokeStatic handle the corresponding class is not initialized initialization.

Other loading conditions

  1. When a Java virtual machine initializes a class, it requires that all of its parent classes be initialized. This rule does not apply to interfaces.

    • When a class is initialized, the interface it implements is not initialized first
    • When an interface is initialized, its parent interface is not initialized first
    • Therefore, a parent interface is not initialized by its child interface or by the class that implements it. The interface is initialized only when a program first uses a static variable of a particular interface.
  2. Active use of an interface or class is considered only if static variables or methods accessed by the current program are actually defined by the current class or interface.

  3. Calling the loadClass method of the ClassLoader class to load a class is not an active use of the class and does not result in class initialization.

Test Example 1:

public class Test_2 extends Test_2_A {
    static {
        System.out.println("Subclass static code block");
    }

    {
        System.out.println("Subclass code block");
    }

    public Test_2(a) {
        System.out.println("Subclass Constructor");
    }

    public static void main(String[] args) {
        newTest_2(); }}class Test_2_A {

    static {
        System.out.println("Superclass static code block");
    }

    {
        System.out.println("Parent code block");
    }

    public Test_2_A(a) {
        System.out.println("Parent class constructor");
    }

    public static void find(a) {
        System.out.println("Static method"); }}// Code block and constructor execution order
//1). Superclass static code block
//2). Subclass static code block
//3). Parent code block
//4). Parent class constructor
//5). Subclass code block
//6). Subclass constructor
Copy the code

Test Example 2:

public class Test_1 {
    public static void main(String[] args) { System.out.println(Test_1_B.str); }}class Test_1_A {
    public static String str = "A str";

    static {
        System.out.println("A Static Block"); }}class Test_1_B extends Test_1_A {
    static {
        System.out.println("B Static Block"); }}// Output the result
//A Static Block
//A str
Copy the code

loading

The bytecode file is searched on the hard disk and read through IO. When the Class is used, it will be loaded, such as calling the main method, calling the object with the new keyword, etc. During the loading stage, the java.lang.Class object of this Class will be generated in memory as the access point of various data of this Class in the method area.

validation

Verify the correctness of bytecode files

To prepare

Allocate memory for static variables of the class and assign default values

parsing

Will symbols refer to replace with direct reference to the node will put some static methods (symbol references, such as the main () method) to replace for the pointer to the data storage, memory, or a handle, etc. (reference) directly, this is the so-called static linking process (complete) during class loading, dynamic linking is done during the program is running will use replacement for direct reference symbols.

Initialize the

Initialize a static variable of a class to the specified value, executing a static code block.

Class loader

  • The Bootstrap Class Loader is responsible for loading<JAVA_HOME>\lib\Directory or by-DbootclaspathArgument to specify the class, such as rt.jar, tool.jar, etc.
  • The Extension Class Loader is responsible for loading<JAVA_HOME>\lib\ext\-Djava.ext.dirsThe classes and JAR packages in the directory specified by the option.
  • The application Class Loader is responsible for loadingCLASSPATH-Djava.class.pathClasses and JAR packages in the specified directory.
  • Custom Class loader: loads user-defined Class packages in the package path and implements Class loading through subclasses of ClassLoader.

Test file:

public class TestJVMClassLoader {

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

        System.out.println();
        ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
        ClassLoader extClassLoader = appClassLoader.getParent();
        ClassLoader bootstrapClassLoader = extClassLoader.getParent();

        System.out.println("bootstrapClassLoader: " + bootstrapClassLoader);
        System.out.println("extClassLoader: " + extClassLoader);
        System.out.println("appClassLoader: " + appClassLoader);

        System.out.println();
        System.out.println("BootstrapLoader loads the following files:");
        URL[] urls = Launcher.getBootstrapClassPath().getURLs();
        for (URL url : urls) {
            System.out.println(url);
        }
        System.out.println();
        System.out.println("ExtClassLoader loads the following files:");
        System.out.println(System.getProperty("java.ext.dirs"));
        System.out.println();
        System.out.println("AppClassLoader loads the following files:");
        System.out.println(System.getProperty("java.class.path")); }}Copy the code

Parent delegation mechanism

What is the parent delegate mechanism?

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

The class loading and parental delegation models are shown below

Let’s look again at the loadClass method of the ClassLoader class

// loadClass
protectedClass<? > loadClass(String name,boolean resolve)
  throws ClassNotFoundException
{
  synchronized (getClassLoadingLock(name)) {
    // First check if the current class is loadedClass<? > c = findLoadedClass(name);if (c == null) {
      long t0 = System.nanoTime();
      try {
        if(parent ! =null) {
          // If the parent class loader is not empty, try the parent class load first
          c = parent.loadClass(name, false);
        } else {
          // The boot class loader attempts to loadc = findBootstrapClassOrNull(name); }}catch (ClassNotFoundException e) {
        // ClassNotFoundException thrown if class not found
        // from the non-null parent class loader
      }

      if (c == null) {
        // If still not found, then invoke findClass in order
        // to find the class.
        long t1 = System.nanoTime();
        // Try to load it yourself
        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; }}// Class loader inclusion relationship
public abstract class ClassLoader {

    private static native void registerNatives(a);
    static {
        registerNatives();
    }
		
  	// Contains the current ClassLoader and parent ClassLoader
    private final ClassLoader parent;
  
}
Copy the code

Conclusion:

  1. Not a tree (just a logical tree), but an include/wrap relationship.
  2. Load order, application class loader, extension loader, system loader.
  3. If there is a class loader that can load successfullyTestClass, then the classloader is calledDefine the class loaderAll Class loaders (including defining Class loaders) that may return a reference to a Class object are called initial Class loaders.

What is the purpose of parental delegation?

  1. Ensure type safety of Java core libraries: All Java applications reference at least the Java.lang. Object class, which means that at runtime, the java.lang.Object class is loaded into the Java virtual machine. If this loading is done by the Java application’s own classloader, It is likely that there will be multiple versions of the Java.lang. Object class in the JVM, and that they will not be compatible with each other. With the help of the parent delegate mechanism, class loading in the Java core library is done uniformly by starting the class loader. This ensures that Java applications are using the same version of the Java core libraries and that they are compatible with each other.

  2. You can ensure that classes provided by the Java core library are not replaced by custom classes.

  3. Different classloaders can create additional namespaces for classes of the same class (binary name). Classes with the same name can coexist in the Java virtual machine, requiring only different classloaders to load them. Classes of different classloaders are incompatible with each other, which creates separate Java class Spaces within the Java Virtual machine. This technique is used in many frameworks.

Custom class loaders

Custom class loaders load classes. Here is a simple Demo

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

public class ClassLoaderTest extends ClassLoader {

    private static String rxRootPath;

    static {
        rxRootPath = "/temp/class/";
    }

    @Override
    public Class findClass(String name) {
        byte[] b = loadClassData(name);
        return defineClass(name, b, 0, b.length);
    }

    /** * Reads the.class file as a byte array **@paramName Full path class name *@return* /
    private byte[] loadClassData(String name) {
        try {
            String filePath = fullClassName2FilePath(name);
            InputStream is = new FileInputStream(new File(filePath));
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] buf = new byte[2048];
            int r;
            while((r = is.read(buf)) ! = -1) {
                bos.write(buf, 0, r);
            }
            return bos.toByteArray();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }

    /** * fully qualified name converted to file path **@param name
     * @return* /
    private String fullClassName2FilePath(String name) {
        return rxRootPath + name.replace("."."/ /") + ".class";
    }

    public static void main(String[] args) throws ClassNotFoundException {
        ClassLoaderTest classLoader = new ClassLoaderTest();
        String className = "com.test.TestAA";

        Class clazz = classLoader.loadClass(className);
        System.out.println(clazz.getClassLoader());
        // Output the result
        //cn.xxx.xxx.loader.ClassLoaderTest@3764951d}}Copy the code

Tomcat class loader

The class loader model in Tomcat

Description of the Tomcat class loader

The main class loaders for Tomcat:

  • CommonLoader: Basic Tomcat class loader. Classes in the loading path can be accessed by the Tomcat container itself and various WebApps.

  • CatalinaLoader: Classes in the private class loader path of the Tomcat container are not visible to Webapp.

  • SharaLoader: Class loader shared by various WebApps. The class in the loading path is visible to all WebApps, but not to the Tomcat container.

  • webappLoader: The classes in the loading path are only visible to the current Webapp. For example, the classes in the loading path are only visible to the current Webapp. Each WAR application has its own webappClassLoader object, corresponding to different namespaces, which are isolated from each other. For example, the WAR package can introduce different spring versions, enabling multiple Spring versions to run at the same time.

Conclusion:

As you can see from the delegate relationship in the figure:

Classes that can be loaded by Commonclassloader can be used by Catalinaclassloader and Sharedclassloadert. The classes that Catalinaclassloader and Sharedclassloader can load by themselves are isolated from each other. Webappclassloader can use the Shared Loader to load classes, but each class Webappclassloader instances are isolated from each other, and Jasper Loader only loads the.class file that the JSP files compile, which appears to be discarded: When the Web container detects that the JSP file has been modified, it replaces the current Jasperloader instance and implements hot loading of the JSP file by creating a new JSP classloader.

Does Tomcat’s classloading mechanism break Java’s recommended parental delegation model? The answer is: yes

Tomcat is not implemented like this. Tomcat does not comply with this convention to achieve isolation. Each WebApp Loader loads the class file in its own directory and does not pass it to the parent class Loader, breaking the parent delegate mechanism

The resources

  1. Deep Understanding of the Java Virtual Machine, 3rd edition, zhiming Zhou

  2. Apache Tomcat Documentation