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

foreword

This time, another important part of the JVM is brought, the class loading mechanism, no nonsense, directly open.

text

1. The process of class 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.

1) Load

A stage of the "class loading" process. During the loading stage, the virtual machine needs to do 3 things:

  • Get the binary byte stream defining this class by its fully qualified name.
  • Convert the static storage structure represented by this byte stream into the runtime data structure of the method area.
  • A java.lang.Class object representing this class is generated in memory as an access entry for various data of this class in the method area.

2) Verify

The first step of the connection phase, the purpose of this phase is to ensure that the information contained in the byte stream of the Class file meets the requirements of the current virtual machine, and will not compromise 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) Prepare

This stage is the stage of formally allocating memory for class variables (static modified variables) and setting the initial values ​​of class variables. The memory used by these variables will be allocated in the method area. The initial value mentioned here is "usually" the zero value of the data type. The following table lists the zero value of all basic data types in Java.

4) Parse

This phase is the process by which the virtual machine replaces symbolic references in the constant pool with direct references. The parsing action is mainly performed for seven types of symbolic references, namely, classes or interfaces, fields, class methods, interface methods, method types, method handles, and call site qualifiers.

5) Initialize

At the initialization stage, the Java program code defined in the class is actually executed. In the preparation stage, the variable has been assigned the initial zero value required by the system, and in the initialization stage, the class variables and other resources will be initialized according to the subjective plan made by the programmer through the program.

We can also express it in another, more direct form: the initialization phase is the process of executing the class constructor <clinit>() method. <clinit>() is not a method that programmers write directly in Java code, but is automatically generated by the Javac compiler.

The <clinit>() method is generated by the compiler automatically collects assignments of all class variables in the class and merges the statements in the static statement block (static{} block). The order of the compiler collection is determined by the statement in the source file. Depending on the order of appearance, only the variables defined before the static statement block can be accessed in the static statement block, and the variables defined after it can be assigned values ​​in the preceding static statement block, but cannot be accessed.

I also wrote an interview question about initialization before: an interesting "initialization" interview question , and interested students can take a look. 

2. What class loaders are in the Java virtual machine?

From the perspective of the Java virtual machine, there are only two different class loaders:

One is the Bootstrap ClassLoader, which is implemented in C++ and is part of the virtual machine itself;

The other is all other class loaders, which are implemented by the Java language, independent of the outside of the virtual machine, and all inherit from the abstract class java.lang.ClassLoader.

From the perspective of Java developers, most Java programs use the class loaders provided by the following three systems.

1) Start the class loader (Bootstrap ClassLoader):

This class loader is responsible for storing the files stored in the <JAVA_HOME>\lib directory, or in the path specified by the -Xbootclasspath parameter, and recognized by the virtual machine (only recognized by the file name, such as rt.jar, the name does not match The class library is not loaded even if placed in the lib directory) The class library is loaded into the virtual machine memory.

2) Extension ClassLoader (Extension ClassLoader):

This loader is implemented by sun.misc.Launcher$ExtClassLoader, which is responsible for loading all class libraries in the <JAVA_HOME>\lib\ext directory or in the path specified by the java.ext.dirs system variable. Developers can directly Use extension class loader.

3) Application ClassLoader (Application ClassLoader):

This class loader is implemented by sun.misc.Launcher$AppClassLoader. Since this class loader is the return value of the getSystemClassLoader() method in ClassLoader, it is generally called the system class loader. It is responsible for loading the class library specified on the user's classpath (ClassPath). Developers can use this class loader directly. If the application has not customized its own class loader, in general, this is the default class in the program. Loader.

Our applications are loaded by these three class loaders. If necessary, we can add our own defined class loaders. The relationship between these class loaders is generally as shown.

3. What is the parental delegation model?

If a class loader receives a class loading request, it will not try to load the class itself first, but delegate the request to the parent class loader to complete. This is the case for every level of class loaders, so all The load request should eventually be passed to the top-level startup class loader, and only when the parent loader reports that it cannot complete the load request (the required class is not found in its search scope), the child loader will try itself to load.

The source code for class loading is as follows:

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 1、检查请求的类是否已经被加载过了
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    // 2、将类加载请求先委托给父类加载器
                    if (parent != null) {
                        // 父类加载器不为空时,委托给父类加载进行加载
                        c = parent.loadClass(name, false);
                    } else {
                        // 父类加载器为空,则代表当前是Bootstrap,从Bootstrap中加载类
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // 如果父类加载器抛出ClassNotFoundException
                    // 说明父类加载器无法完成加载请求
                }

                if (c == null) {
                    // 3、在父类加载器无法加载的时候,再调用本身的findClass方法来进行类加载
                    long t1 = System.nanoTime();
                    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;
        }
    }

4. Why use the parental delegation model?

1) Using the parent delegation model to organize the relationship between class loaders, there is an obvious benefit that Java classes have a priority hierarchy along with their class loaders.

2) If the parent delegation model is not used, and each class loader loads it by itself, if the user writes a java.lang.Object class and puts it in the ClassPath of the program, there will be multiple differences in the system. Object class, the most basic behavior in the Java type system is not guaranteed, and the application will become a mess.

5. What scenarios break the parental delegation model?

 The most common scenarios are:

1) Thread context class loader, typical: JDBC uses thread context class loader to load Driver implementation class

2) Tomcat's multi-web application

3) OSGI realizes modular hot deployment

6. Why break the parental delegation model?

The reason is actually very simple, that is, the use of the parent delegation model cannot meet the needs, so it can only be destroyed. Here, take Tomcat, which is often asked in interviews, as an example.

We know that the Tomcat container can deploy multiple web applications at the same time, and it is easy for multiple web applications to depend on the same jar package but with different versions. For example, application 1 and application 2 both depend on spring, application 1 uses version 3.2.*, and application 2 uses version 4.3.*.

If following the parental delegation model, which version is used at this time?

In fact, which version can not be used, it is prone to compatibility problems. Therefore, Tomcat can only choose to break the parental delegation model.

7. How to break the parent delegation model?

The idea of ​​​​destroying the parental delegation model is similar. Here is an example of Tomcat, which is often asked in interviews.

In fact, the principle is very simple. We can see that the method modifier of the above class loading method source code (loadClass) is protected, so we only need the following steps to destroy the parent delegation model.

1) Inherit ClassLoader, WebappClassLoader in Tomcat inherits the subclass URLClassLoader 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.

8. Tomcat's class loader?

Tomcat's class loader is shown below:

1) Bootstrap ClassLoader: You can see that the Extension ClassLoader is missing in the above figure. In Tomcat, the Extension ClassLoader is integrated into the Bootstrap ClassLoader.

2) System ClassLoader is Application ClassLoader: The system class loader in Tomcat does not load the contents of the CLASSPATH environment variable, but builds the System class loader from the following resource libraries.

  • $CATALINA_HOME/bin/bootstrap.jar, contains the main() method used to initialize the Tomcat server, and the classloader implementation classes it depends on.
  • $CATALINA_BASE/bin/tomcat-juli.jar or $CATALINA_HOME/bin/tomcat-juli.jar, the logging implementation class.
  • If tomcat-juli.jar exists in $CATALINA_BASE/bin, use it instead of the one in $CATALINA_HOME/bin.
  • $CATALINA_HOME/bin/commons-daemon.jar

3) Common ClassLoader: As can be seen from the name, it mainly contains some common classes, which are visible to Tomcat internal classes and all Web applications.

The locations the class loader searches are defined by the common.loader property in $CATALINA_BASE/conf/catalina.properties, the default setting will search the following locations in order.

  • Unpackaged classes and resources in $CATALINA_BASE/lib
  • JAR files in the $CATALINA_BASE/lib directory
  • Unpackaged classes and resources in $CATALINA_HOME/lib
  • JAR files in the $CATALINA_HOME/lib directory

4) WebappX ClassLoader: Tomcat creates a separate class loader for each deployed Web application, which ensures isolation between different applications, and classes and resources are invisible to other Web applications. The loaded path is as follows:

  • All unpackaged classes and resources in the /WEB-INF/classes directory of the web application
  • Classes and resources in JAR files in the /WEB-INF/lib directory of the web application

9. What is the class loading process of Tomcat?

The class loading process of Tomcat, that is, the logic of WebappClassLoaderBase#loadClass is as follows.

1) First cache resourceEntries locally, and if it has been loaded, return the data in the cache directly.

2) Check whether the JVM has loaded the class, and return directly if so.

3) Check whether the class to be loaded is a Java SE class, and if so, use the BootStrap class loader to load the class to prevent the webapp's class from overwriting the Java SE's class.

For example, you wrote a java.lang.String class and put it in /WEB-INF/classes of the current application. If there is no guarantee of this step, then the String class used in the project will be defined by yourself, not rt .jar below, may lead to many hidden dangers.

4) For the delegate attribute delegate display set to true, or some special classes (javax, some classes under the org package), use the parent delegation mode to load, and only a few parts use the parent delegation model to load.

5) Try to load the class from the local. If the loading fails in step 5, it will go to this step. This breaks the parent delegation model and loads from the local first.

7) Going here, it means that the loading in step 6 failed. If the parent delegation mode was not used before, it will be delegated to the parent class loader to try to load.

8) Going this way means that all attempts to load fail and ClassNotFoundException is thrown.

10. The principle of JDBC using thread context class loader

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, oracle and sqlserver will also have their own implementation jars.

At this time, the basic class of JDBC needs to call the code of the JDBC service provider interface (SPI, Service Provider Interface) implemented by other manufacturers and deployed under the ClassPath of the application. When class A calls class B, class B is loaded by the class loader of class A, and the basic classes of JDBC are loaded by Bootstrap ClassLoader, but Bootstrap ClassLoader does not know and will not load these manufacturers implemented code.

Therefore, Java provides thread context class loader, allowing to set and get the current thread's context class loader through Thread#setContextClassLoader/Thread#getContextClassLoader(). 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.

In summary, JDBC can realize the behavior of parent class loader "delegating" sub-class loader to complete class loading through thread context class loader, which obviously does not comply with the parent delegation model, but this is also the defect of the parent delegation model itself caused.

finally

I am Jon Hui, a programmer who insists on sharing original technical dry goods .

Guess you like

Origin blog.csdn.net/v123411739/article/details/119700990