Do you really understand the JVM class loading mechanism that must be asked in interviews?

foreword

This article brings an important knowledge point of JVM. Maybe students have more or less understanding of JVM memory management, but they have not thought about how the java code we wrote is loaded into memory by JVM virtual machine. ? With doubts, after reading this article, you will gain a lot.

Explore the class loading mechanism

1. The process of loading

The entire life cycle of a class starts from being loaded into the virtual machine memory and unloads the memory, and its entire life cycle includes seven stages: loading, verifying, preparing, parsing, initializing, using, and unloading. Among them, the three parts of verification, preparation and analysis are collectively called connection.
insert image description here

  1. Loading: Loading is a stage in the class loading process

    • Find such a bytecode file by its fully qualified name
    • Create a Class object from a bytecode file.
    • A java.lang.Class object representing this class will be generated in memory as the entry for various data of this class in the method area.
  2. Verification: The first step in the connection phase, the main purpose of this phase is to ensure whether the information contained in the byte stream of the Class file meets the requirements of the current virtual machine, and will not endanger the security of the virtual machine itself. On the whole, the verification stage will generally complete the following four stages of verification actions: file format verification, metadata verification, bytecode verification, and symbol reference verification.

  3. Preparation: semi-initialization, this stage is to formally allocate memory for class variables (modified for static), and set the initial value (0 or null or false)

    Pay attention to the concept of initial value mentioned here, for example, the initial value of int is 0,

    public static int port= 8080;
    

    In fact, the initial value of the variable after the preparation phase is 0 instead of 8080. The put static instruction that assigns the port value to 8080 is stored in the class constructor < client > method after the program is compiled.

  4. Resolution: This phase is the process by which the virtual machine replaces the symbolic references in the constant pool with direct references. (Symbolic references can abstractly understand placeholders, and direct references indicate that there is a real reference relationship loaded into JVM memory)

  5. Initialization: Initialization can be said to be the last step in the class loading phase. When the initialization phase is reached, the Java program code defined in the class is actually executed. In the preparation phase, the variable has been assigned the initial zero value required by the system, and in the initialization phase, the variable or resource will be initialized according to the programmer's assignment.

    The initialization phase is the process of executing the class constructor <client> method <clinit> is not a method written directly by the programmer in the Java code, but is automatically generated by the Javac compiler. The virtual machine ensures that the method of the parent class has been executed before the child <client> method is executed . If there is no static variable assignment or static statement block in a class, the compiler may not generate the <client>() method for this class.

Class initialization is not performed in the following cases:

  • Referencing the static fields of the parent class through the subclass will only trigger the initialization of the parent class, not the subclass.
  • Defining an array of objects does not trigger initialization of the class.
  • Constants are stored in the constant pool of the calling class during compilation. Essentially, there is no direct reference to the class that defines the constant, and the class where the constant is defined will not be triggered.
  • Obtaining the Class object by the class name will not trigger the initialization of the class.
  • When loading the specified class through Class.forName, if the specified parameter initialize is false, the class initialization will not be triggered. In fact, this parameter tells the virtual machine whether to initialize the class.
  • Through the default loadClass method of ClassLoader, the initialization action will not be triggered.

2. Types of class loaders

insert image description here
JDK comes with three class loaders: bootstrap ClassLoader, ExtClassLoader, and AppClassLoader.

  1. BootStrapClassLoader (boot class loader) starts the class loader, which is implemented by C++ and has no parent class. It is the parent class loader of ExtClassLoader. By default, it is responsible for loading jar packages and class files under %JAVA_HOME%lib (mainly loading rt.jar).
  2. ExtClassLoader (extended class loader) is the parent class loader of AppClassLoader, which is responsible for loading jar packages and classes in the %JAVA_HOME%/lib/ext folder.
  3. AppClassLoader (application class loader) is the parent class of custom class loader and is responsible for loading class files under the classpath. System class loader, developers can use this class loader directly, if the application has not customized its own class loader, in general, this is the default class loader in the program.

3. What is parental delegation?

insert image description here

    When a class receives a class loading request, it will first not try to load the class itself, but delegate the request to the parent class to complete it. This is the case for every class loader in the hierarchy, so all loading requests should be sent. In the startup class loading, only when the parent class loader reports that it cannot complete the request (the class to be loaded is not found in its loading path), the child class loader will try to load it by itself .
    One advantage of using parental delegation is that, for example, when loading the class java.lang.Object located in the rt.jar package, no matter which loader loads this class, it is ultimately delegated to the top-level startup class loader for loading, which ensures that Using different class loaders will end up with the same Object object.

advantage:

  1. Avoid duplicate loading of classes
  2. Avoid tampering with Java's core API

The source code for class loading is as follows (java.lang.ClassLoader):

protected Class<?> loadClass(String name, boolean resolve)
      throws ClassNotFoundException
  {
    
    
      synchronized (getClassLoadingLock(name)) {
    
    
          // 先从缓存查找该class对象,找到就不用重新加载
          Class<?> 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) {
    
    
                  // 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
                  // 如果都没有找到,则通过自定义实现的findClass去查找并加载
                  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;
      }
  }

As shown in the loadClass source code, when a class loading request comes, first look up the class object from the cache, and return it directly if it exists. Give it to the top-level startup class loader to load, and if it is still not found, use the findClass() method to load the custom class loader.

4. What scenarios break the parental delegation model?

The most common scenarios are:

  1. Multiple Web Applications with Tomcat
  2. OSGI implements modular hot deployment
  3. JDBC loads Driver using thread context class loader

5. Why break the parental delegation model?

The parent delegation model cannot meet the business needs. Let's take tomcat as an example.
We all know that the Tomcat container can deploy multiple web applications at the same time, and each application has its own class. If different applications share a class loader, it may cause class conflicts (the full class name is the same, but the method attributes are completely Not the same, each application corresponds to an instance of a class loader, which is actually equivalent to physical isolation)

6. How tomcat breaks the parental delegation model

    Since Tomcat does not follow the parental delegation mechanism, is there any risk if I define a malicious HashMap or String by myself?

    Obviously, tomcat has been running stably for so many years, and obviously there is no such problem. Although tomcat breaks the parent delegation model, the top-level class loader is still the same.
insert image description here
The idea of ​​​​destroying the parent delegation model is similar. In fact, the principle is very simple. The method modifier of loadClass in the source code above is protected, and only two steps are required.

  1. Inheriting ClassLoader, WebAppClassLoader in Tomcat inherits URLClassLoader, a subclass of ClassLoader.
  2. Rewrite the loadClass method to implement your own logic. Don't delegate loading to the parent class every time. For example, you can load it locally first, which will destroy the parent delegation model.

Each Tomcat's WebAppClassLoader first loads the class files in its own directory, and will not pass it to the parent class loader. If it cannot be loaded, it will be handed over to commonClassLoade for parental delegation.

Summary of Broken Parental Delegation

  • To avoid class conflicts, there cannot be a situation where a class library loaded in one application affects another application. The class library used in each webapp project must have an isolation mechanism
  • Different webapp projects support sharing certain class libraries

7. JDBC breaks the parent delegation model

The class loader is limited by the loading scope. In some cases, the parent class loader cannot load the required file. In this case, the sub class loader needs to be delegated to load the class file.

The basic classes related to JDBC functions are uniformly defined by Java. In rt.jar, such as DriverManager, which is loaded by Bootstrap ClassLoader, the implementation classes of JDBC are in the implementation jar packages of various manufacturers. For example, MySQL is in mysql. In -connector-java, various data vendors will also have different jar packages. According to the class loading mechanism, when the loaded class references another class, the virtual machine will use the class loader that loads the first class. Load the referenced class . At this time, only the Driver interface of rt.jar is loaded, and the mysql implementation class is not loaded. At this time, the subclass loader needs to load the Driver implementation, which destroys the parent delegation model.

  • Thread context class loader is available in Java
//设置或者获取当前线程的上下文类加载器
 ClassLoader cl = Thread.currentThread().getContextClassLoader();
  Thread.currentThread().setContextClassLoader(this.loader);

Sets or gets the context class loader of the current thread. If it is not set when the thread is created, it will inherit the parent thread. If it is not set in the global scope of the application, the class loader is the Application ClassLoader by default.

Therefore, JDBC can use the thread context class loader to realize the behavior of the parent class loader "delegating" the child class loader to complete class loading, which obviously does not comply with the parent delegation model, but this is also caused by the defects of the parent delegation model itself. of.

finally

I'm Xiaoli, a back-end blogger who insists on sharing dry goods.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=324083791&siteId=291194637