Detailed explanation of JVM class loading mechanism

1. Class life cycle

                       

analyze:

1) Loading: Find and import class files

       It does not have to be obtained from a Class file. It can be read from a ZIP package (such as from a jar package and a war package), calculated at runtime (dynamic proxy), or generated by other files. (For example, converting the JSP file into the corresponding Class class).

2) Verification: Check the correctness of the data in the class file

        In order to ensure whether the information contained in the byte stream of the Class file meets the requirements of the current virtual machine, and does not endanger the security of the virtual machine itself.

3) Prepare preparation: Allocate storage space for static variables of the class, and set initial values;

        The initial value concept mentioned here, for example, a class variable is defined as:    

public static int i = 9999;

       In fact, the initial value of the variable v after the preparation phase is 0 instead of 9999. The putstatic instruction that assigns v to 8080 is stored in the class constructor <client> method after the program is compiled, which we will explain later.
        But note that if the declaration is:

public static final int i = 9999;

       In the compilation phase, the ConstantValue property will be generated for v, and in the preparation phase, the virtual machine will assign the value of v to 9999 according to the ConstantValue property.

4) Resolution resolution: refers to the process by which the virtual machine replaces the symbolic references in the constant pool with direct references           

5) Initialization: In addition to customizing the class loader during the loading phase, other operations are dominated by the JVM. At the initial stage, the actual execution of the Java program code defined in the class begins.

JVM initialization steps

1、假如这个类还没有被加载和连接,则程序先加载并连接该类

2、假如该类的直接父类还没有被初始化,则先初始化其直接父类

3、假如类中有初始化语句,则系统依次执行这些初始化语句

2. Class loader

1. Class loading

        Read the binary data in the .class file of the class into memory, put it in the method area of ​​the runtime data area, and then create a java.lang.Class object in the heap area to encapsulate the class in the method area data structure. The final product of class loading is the Class object located in the heap area. The Class object encapsulates the data structure of the class in the method area and provides Java programmers with an interface to access the data structure in the method area. 

        The class loader does not need to wait until a class is "actively used for the first time" to load it, the JVM specification allows the class loader to pre-load a class in anticipation that it will be used, if encountered during the pre-loading process. If the .class file is missing or has errors, the class loader must report an error when the program actively uses the class for the first time (LinkageError error). If the class has not been actively used by the program, the class loader will not report an error.

2. Class loader

      The virtual machine design team implements the loading action outside the JVM, so that the application can decide how to obtain the required classes. The JVM provides three class loaders:

  • Bootstrap ClassLoader: Responsible for loading classes in the JAVA_HOME\lib directory, or in the path specified by the -Xbootclasspath parameter, and recognized by the virtual machine (identified by file name, such as rt.jar).
  • Extension ClassLoader: Responsible for loading the class library in the JAVA_HOME\lib\ext directory, or in the path specified by the java.ext.dirs system variable.
  • Application class loader (Application ClassLoader): responsible for loading the class library on the user path (classpath).                      

The JVM loads classes through the parent delegation model. Of course, we can also implement custom class loaders by inheriting java.lang.ClassLoader.

Two loading class scenarios:

  1. Implicit loading: Instead of calling ClassLoader in the code to load the required classes, the JVM automatically loads the required classes into memory. For example, when a class inherits or references a class, the JVM resolves that the current class is not present. When in memory, these classes are automatically loaded into memory.
  2. Explicit loading: Load a class through the ClassLoader class in code, such as calling this.getClass.getClassLoader().loadClass() or Class.forName().

The ways to load .class files are:

1. Load directly from the local system
2. Download the .class file from the Internet
3. Load .class files from archives like zip, jar, etc.
4. Extract .class files from proprietary database
5. Compile Java source files dynamically into .class files

 

                 

The process of loading classes by ClassLoader

  1. Find the .class file and load this file into memory
  2. Bytecode verification, class data structure analysis, memory allocation and symbol table linking
  3. Static properties and initialization assignments in classes and execution of static code blocks

Trigger conditions for class initialization: Class initialization occurs only when the class is actively used.

    (1) When encountering the four bytecode instructions of new, getstatic, putstatic or invokestatic, if the class has not been initialized, its initialization needs to be triggered first. The most common Java code scenario that generates these 4 instructions is: when instantiating an object with the new keyword, reading or setting a static field of a class (modified by final, a static field that has put the result into the constant pool at compile time) fields), and when calling a static method of a class.

   (2) When using the method of the java.lang.reflect package to make a reflection call to a class, if the class has not been initialized, its initialization needs to be triggered first.

    (3) When initializing a class, if it is found that its parent class has not been initialized, it needs to trigger the initialization of its parent class first.

    (4) When the virtual machine starts, the user needs to specify a main class to be executed (the class containing the main() method), and the virtual machine will initialize this main class first

When a class loader receives a class loading task, it will first hand it over to its parent class loader to complete, so the final loading task will be passed to the top-level startup class loader, only when the parent class loader cannot complete the loading task, will attempt to perform the load task.

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.

The source code implementation of ClassLoader in jdk:

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        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
                // to find the class.
                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;
    }
}
  • The implementation of the above findClass() is as follows, throwing an exception directly, and the method is protected, obviously this is left to our developers to implement.
  • First through Class c = findLoadedClass (name); to determine whether a class has been loaded.
  • If the program in if (c == null) has not been loaded, it follows the model of parental delegation and will first search from the parent loader through recursion until the parent class loader is Bootstrap ClassLoader.
  • Finally, according to the value of resolve, determine whether the class needs to be resolved.
protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
}

Parent delegation model

Parent delegation model process

When a specific class loader receives a request to load a class, it first delegates the loading task to the parent class loader, recursively, and returns successfully if the parent class loader can complete the class loading task; only the parent class loader When this loading task cannot be completed, it will be loaded by itself.

The advantage of using the parent delegation model is that Java classes have a prioritized hierarchy with their class loader . For example, the class java.lang.Object exists in rt.jar. No matter which class loader wants to load this class, it is finally delegated to the Bootstrap ClassLoader at the top of the model for loading. Therefore, the Object class is used in various programs of the program. The class loader environment is all the same class. On the contrary, if there is no parent delegation model but each class loader loads it by itself, if the user writes a class with the same name as java.lang.Object and puts it in the ClassPath, there will be multiple different Object classes in the system. The program will be messed up. Therefore, if a developer tries to write a Java class with the same name as the rt.jar class library, it will compile normally, but will never be loaded and run.

System Implementation of Parent Delegation Model

In the loadClass() method of java.lang.ClassLoader, first check whether it has been loaded, if not, call the loadClass() method of the parent class loader. If the parent loader is empty, the startup class loader is used by default as the parent loader. If the parent fails to load, a ClassNotFoundException is thrown, and then its own findClass() method is called to load.

3. Custom class loader

       1. Why customize the class loader?

  The class loader provided by the JVM can only load jars and classes in the specified directory. If we want to load classes or jars in other locations, such as loading a class file on the network, the default ClassLoader cannot meet our needs, so You need to define your own classloader.

  2. How to implement a custom class loader?

  We implement a ClassLoader and specify the loading path of this ClassLoader. There are two ways:

  Method 1 : Inherit ClassLoader and override the findClass() method of the parent class. The code is as follows:

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class PathClassLoader extends ClassLoader
{
    public static final String drive = "d:/";
    public static final String fileType = ".class";

    public static void main(String[] args) throws Exception
    {
        PathClassLoader loader = new PathClassLoader();
        Class<?> objClass = loader.loadClass("HelloWorld", true);
        Object obj = objClass.newInstance();
        System.out.println(objClass.getName());
        System.out.println(objClass.getClassLoader());
        System.out.println(obj.getClass().toString());
    }

    public Class<?> findClass(String name)
    {
        byte[] data = loadClassData(name);
        return defineClass(name, data, 0, data.length);// 将一个 byte 数组转换为 Class// 类的实例
    }
    public byte[] loadClassData(String name)
    {
        FileInputStream fis = null;
        byte[] data = null;
        try
        {
            fis = new FileInputStream(new File(drive + name + fileType));
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int ch = 0;
            while ((ch = fis.read()) != -1)
            {
                baos.write(ch);
            }
            data = baos.toByteArray();
        } catch (IOException e)
        {
            e.printStackTrace();
        }
        return data;
    }
}

The main method calls the loadClass() method of the parent class, which uses the specified binary name to load the class. The following is the source code of the loadClass method:

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name))
        {
            // 第一步先检查这个类是否已经被加载
            Class<?> c = findLoadedClass(name);
            if (c == null)
            {
                long t0 = System.nanoTime();
                try
                {
                    //parent为父加载器
                    if (parent != null)
                    {
                        //将搜索类或资源的任务委托给其父类加载器
                        c = parent.loadClass(name, false);
                    } else
                    {
                        //检查该class是否被BootstrapClassLoader加载
                        c = findBootstrapClassOrNull(name);
                    }
                } 
                catch (ClassNotFoundException e)
                {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }
                if (c == null)
                {
                    //如果上述两步均没有找到加载的class,则调用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;
        }
    }

       This method first checks whether the specified class has been loaded. If it has been loaded, it calls the resolveClass() method to link the specified class. If it has not been loaded, it first delegates the task of searching for a class or resource to its parent class loader. Check whether the class is loaded by BootstrapClassLoader. If the loaded class is not found in the above two steps, call the findClass() method. In our custom loader, we rewrite the findClass method to load the class in the path we specified. document.

  In addition, our custom class loader does not specify a parent loader. If the parent class loader is not specified in the JVM specification, the system class loader, namely AppClassLoader, is used as its parent loader by default, so when using this custom class When the loader is used, the class to be loaded cannot be in the classpath, otherwise, according to the principle of the parent delegation model, the class to be loaded will be loaded by the system class loader. If you must put the classes that the custom loader needs to load on the classpath, set the parent loader of the custom class loader to null. 

  Method 2 : Inherit the URLClassLoader class, and then set the URL of the custom path to load the class under the URL.

  We convert the specified directory to a URL path and then override the findClass method.

4. End the life cycle

In the following cases, the Java virtual machine ends its life cycle 
1. The System.exit() method 
is executed 2. The program execution ends normally 
3. The program encounters an exception or error during execution and terminates abnormally 
4. Due to an error in the operating system And cause the Java virtual machine process to terminate

 

refer to:

https://www.cnblogs.com/xujian2014/p/5551153.html

http://www.importnew.com/25295.html

https://blog.csdn.net/fgets/article/details/52934178

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325475394&siteId=291194637