JVM class loading process parsing

Posted by only one on Thu, 20 Jan 2022 23:22:12 +0100

WeChat official account: operation and development story, Lao Zheng

Class loading process

Class loading timing

The whole life cycle of a type will go through seven stages: loading, verification, preparation, parsing, initialization, use and unloading. Where validation, preparation, and resolution are connections

Seven cases in which classes are actively loaded

  1. Create an instance of a class, such as: new Object();

  2. Accessing or assigning a value to a static variable of a class or interface;

  3. Call the static method of the class;

  4. Reflection (such as Class.forName("com.test.Test");

  5. Initialize subclasses of a class;

  6. When the Java virtual machine is started, the class marked as the startup class is the class containing the main method (Java Test);

  7. JDK1.7 start to provide dynamic language support, Java lang.invoke. Parsing result of methodhandle instance REF_getStatic, REF_putStatic,;

REF_ If the class corresponding to the invokestatic handle is not initialized, it will be initialized.

Other loading conditions

  1. When Java virtual machine initializes a class, all its parent classes are required to be initialized. This rule alone does not apply to interfaces.
  • When initializing a class, the interface it implements is not initialized first

  • When initializing an interface, its parent interface is not initialized first

  • Therefore, a parent interface will not be initialized because of its child interface or class initialization. It will be initialized only when the program uses the static variables of a specific interface for the first time.

  1. Only when the static variable or static method accessed by the current program is indeed defined in the current class or current interface, it can be considered as an active use of the interface or class.

  2. Calling the loadClass method of ClassLoader class to load a class is not an active use of the class and will not lead to 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() {
        System.out.println("Subclass construction method");
    }

    public static void main(String[] args) {
        new Test_2();
    }

}

class Test_2_A {

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

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

    public Test_2_A() {
        System.out.println("Parent class construction method");
    }

    public static void find() {
        System.out.println("Static method");
    }
}

//Code block and construction method execution order
//1). Parent static code block
//2). Subclass static code block
//3). Parent code block
//4). Parent class construction method
//5). Subclass code block
//6). Subclass construction method

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 results
//A Static Block
//A str

Class loading process

load

Find the bytecode file on the hard disk and read it through IO. The class will be loaded only when it is used. For example, call the main method, call the object with the new keyword, etc. in the loading stage, the Java. Java of this class will be generated in memory Lang. class object, as the access entry for various data of this class in the method area.

verification

Verify the correctness of bytecode file

prepare

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

analysis

Replace the symbolic reference with a direct reference. This node will replace some static methods (symbolic reference, such as the main() method) with pointers or handles to the memory stored in the data (direct reference). This is the so-called static linking process (completed during class loading). Dynamic linking is completed during program running, replacing the symbolic reference with a direct reference.

initialization

Initialize the static variable of the class to the specified value and execute the static code block.

Class loader

  • **_ Bootstrap Class Loader_** Responsible for loading < Java_ Home > \ lib \ directory or the class specified by the - Dbootclaspath parameter, such as: rt.jar, tool Jar, etc.

  • The Extension Class Loader is responsible for loading < Java_ Home > \ lib \ ext \ or - DJava The classes and jar packages in the directory specified by the ext.dirs option.

  • The application class loader is responsible for loading CLASSPATH or - DJava class. The classes and jar packages in the directory specified by path.

  • Custom Class loader: it is responsible for loading Class packages under the user-defined package path, and loading classes through subclasses of ClassLoader.

Test documents:

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 Load the following files:");
        URL[] urls = Launcher.getBootstrapClassPath().getURLs();
        for (URL url : urls) {
            System.out.println(url);
        }
        System.out.println();
        System.out.println("extClassLoader Load the following files:");
        System.out.println(System.getProperty("java.ext.dirs"));
        System.out.println();
        System.out.println("appClassLoader Load the following files:");
        System.out.println(System.getProperty("java.class.path"));
    }
}

Parental delegation mechanism

What is the parental delegation mechanism?

When a class loader receives a class loading request, it will not try to load the class by itself, but delegate the request to the parent class loader to complete it. This is true for class loaders at each level, so all requests should eventually be transmitted to the top-level startup class loader, Only when the parent loader reports that it cannot complete the load request (that is, the required class is not found in the search scope), the child loader will try to complete the load by itself.

The class loading and parental delegation models are shown in the following figure

Let's take a look at the loadClass method of the ClassLoader class

// loadClass
protected Class<?> loadClass(String name, boolean resolve)
  throws ClassNotFoundException
{
  synchronized (getClassLoadingLock(name)) {
    // First, check whether the current class is loaded
    Class<?> c = findLoadedClass(name);
    if (c == null) {
      long t0 = System.nanoTime();
      try {
        if (parent != null) {
          // If the parent class loader is not empty, try loading the parent class first
          c = parent.loadClass(name, false);
        } else {
          // Boot class loader attempted to load
          c = 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 loading it yourself 
        c = findClass(name);

        // 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;
  }
}


// Inclusion relationship of class loader
public abstract class ClassLoader {

    private static native void registerNatives();
    static {
        registerNatives();
    }
  
   // Inclusion relationship between current ClassLoader and parent ClassLoader
    private final ClassLoader parent;
  
}

Summary:

  1. It is not a tree structure (just a logical tree structure), but an inclusion / packaging relationship.

  2. Loading sequence, application class loader, extended loader, system loader.

  3. If a Class loader can successfully load the Test Class, this Class loader is called the definition Class loader, and all Class loaders that may return Class object references (including the definition Class loader) are called the initial Class loader.

What is the purpose of designing parent delegation mechanism?

  1. Ensure the type safety of Java core library: all Java applications will reference at least Java Lang. object class, that is, at runtime, Java This class of lang.Object will be loaded into the Java virtual machine. If the loading process is completed by the Java application's own class loader, there are likely to be multiple versions of Java. Object in the JVM Lang. object class, and these classes are still incompatible. Invisible to each other (it is the namespace that plays a role) with the help of the two parent delegation mechanism, the class loading in the Java core library is uniformly completed by the startup class loader. This ensures that Java applications use the same version of Java core class libraries, which are compatible with each other.

  2. It can ensure that the classes provided by the Java core library will not be replaced by custom classes.

  3. Different class loaders can create additional namespaces for classes of the same class (binary name). Classes with the same name can exist in the Java virtual machine and only need different class loaders to load them. The classes of different class loaders are incompatible, which is equivalent to creating one isolated Java class space after another in the Java virtual machine. This kind of technology has been actually used in many frameworks.

Custom class loader

The custom class loader loads 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);
    }

    /**
     * Read class file is a byte array
     *
     * @param name 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;
    }

    /**
     * Convert fully qualified names to file paths
     *
     * @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 results 
        //cn.xxx.xxx.loader.ClassLoaderTest@3764951d
    }
}

Tomcat class loader

Class loader model in Tomcat

Tomcat class loader description

Several main class loaders of tomcat:

  • commonLoader: Tomcat's most basic class loader. Classes in the loading path can be accessed by the Tomcat container itself and various webapps.

  • catalinaLoader: the classes in the loading path of the private class loader of the Tomcat container are not visible to the Webapp;

  • sharaLoader: the class loader shared by each webapp. The classes in the loading path are visible to all webapps, but not to the Tomcat container.

  • webappLoader: the private classes of each webapp are loaded. The classes in the loading path are only visible to the current webapp. For example, when loading the relevant classes in the war package, each war package application has its own webappClassLoader object, corresponding to different namespaces and realizing mutual isolation. For example, different spring versions can be introduced into the war package, Realize the simultaneous running of multiple spring versions of applications.

Summary:

As can be seen from the delegation relationship in the figure:

All classes that can be loaded by Commonclassloader can be used by Catalinaclassloader and Sharedclassloader T, so as to realize the sharing of public class libraries. The classes that Catalinaclassloader and Sharedclassloader can load are isolated from each other. Webapp classloader can use the classes that Shared Loader can load, However, each Webappclassloader instance is isolated from each other, and the loading range of Jasper Loader is only the one compiled by this JSP file Class file, which appears to be discarded: when the Web container detects that the JSP file has been modified, it will replace the current Jasperloader instance, and realize the hot loading function of JSP file by establishing a new JSP class loader.

Does the Tomcat loading mechanism violate the parental delegation model recommended by java? The answer is: against

tomcat is not implemented in this way. In order to achieve isolation, tomcat does not abide by this Convention. Each webapp Loader loads the class' file in its own directory and will not pass it to the parent class loader, breaking the two parent delegation mechanism

reference material

  1. In depth understanding of Java virtual machine, Third Edition, Zhou Zhiming

  2. Apache Tomcat Documentation

Official account: operation and development story

github: https://github.com/orgs/sunsharing-note/dashboard

Love life, love operation and maintenance

If you think the article is good, please click on the top right corner to send it to your friends or forward it to your circle of friends. Your support and encouragement is my greatest motivation. If you like, please pay attention to me~

Scanning QR code

Pay attention to me and maintain high-quality content from time to time

reminder

If you like this article, please share it with your circle of friends. For more information, please follow me.

                                          ........................

Topics: Java Back-end