JVM-class loading mechanism


1. The life cycle of a class

After writing a javaclass , a .class(bytecode) file can be obtained after compilation, and this bytecode file needs to be run in the JVM.

insert image description here

The life cycle of a java class refers to the whole process from when a .classfile is loaded into the virtual machine memory to when it is unloaded from the memory. The complete life cycle of a class will go through 加载、连接、初始化、使用和卸载five stages. It 连接also includes 验证、准备、解析these three parts. The order of the five stages of loading, verifying, preparing, initializing, and unloading is determined. The parsing phase is not necessarily, it can start after the initialization phase in some cases, this is to support the runtime binding of the Java language.

insert image description here


Two, the process of class loading

When a program wants to use a certain class, if the class has not been loaded into memory, the system will 类的加载、类的连接、类的初始化initialize through these three steps. In detail, it is 加载、验证、准备、解析、初始化these five parts. If there is no unexpected situation, the JVM will complete these three steps continuously, so these three steps are sometimes collectively referred to as 类加载or 类初始化.

  • Loading: Find the corresponding .classfile and IOwrite it into the method area of ​​the JVM through , and create an java.lang.Classobject
  • Verification: Verify that the loaded .classfile is correct
  • Preparation: Allocate memory space for the variables in the class 静态and save it 初始化为默认值. At this stage, memory is only allocated for static variables (that is, field variables modified by static), and the initial value of the variable is set (for example: static int num = 5; num will be initialized to 0 instead of 5 at this stage), For static finalthe modified variable, it will be allocated at compile time, and the memory of the instance variable will not be allocated
  • Parsing: The process of referencing symbols in the binary data of a class 替换as direct references. A symbolic reference is understood as a mark, and a direct reference directly points to an address in memory
  • Initialization: Initialize the static variables of the class to the specified value and execute the static code block

The underlying detailed process of class loading:

insert image description here


3. Timing of class loading

There is no mandatory constraint on when to load in the virtual machine specification, but the class must be loaded (loaded-initialized) in the following situations:

  • Instantiate an object using the new keyword
  • When reading or setting a static variable of a class
  • When calling the static method corresponding to the class
  • When making a reflective call to a class
  • When initializing a subclass, the parent class will be initialized first
  • When the virtual machine starts, the class that defines the main() method is initialized first

The behavior of the above scenarios is called an active reference to a class. In addition, all methods of referencing a class will not trigger initialization, which is called a passive reference. Common passive references are:

  • Referring to the static fields of the parent class through the subclass will not cause the subclass to be initialized
System.out.println(SubClass.value);  // value 字段在 SuperClass 中定义
  • Referencing a class through an array definition does not trigger initialization of the class. This process will initialize the array class, which is a subclass automatically generated by the virtual machine and directly inherited from Object, which contains the properties and methods of the array
SuperClass[] sca = new SuperClass[10];
  • The constants will be stored in the constant pool of the calling class during the compilation phase. In essence, there is no direct reference to the class that defines the constant, so the initialization of the class that defines the constant will not be triggered.
System.out.println(ConstClass.HELLOWORLD);

4. Class loader

The role of the class loader is to load classthe file into memory and generate the corresponding java.lang.Classobject for it. There are three main class loaders in the JDK, they are 引导类加载器, 扩展类加载器and 应用类加载器. Similarly, we can java.lang.ClassLoaderdo it 自定义类加载器.

Hierarchical relationship of class loaders:

insert image description here

  • Start class loader (bootstrap ClassLoader)
    • It is responsible for loading Java's core class library ((JAVA_HOME/jre/lib/rt.jar, resource.jar or content under the sun.boot.class.path path) to provide the classes needed by the JVM itself.
  • extension class loader (extension ClassLoader)
    • Load the class library from the directory specified by the java.ext.dirs system property, or load the class library from the jre/lib/ext subdirectory (extension directory) of the JDK installation directory. If user-created JARs are placed in this directory, they will also be automatically loaded by the extension class loader
  • Application Loader (application ClassLoader)
    • Responsible for loading .classbytecode , mainly for loading classes written by programmers themselves
  • custom loader
    • Responsible for loading class bytecode files under the programmer-defined path

Is there an inheritance relationship between class loaders?

insert image description here

不是, as can be seen from the above ExtClassLoader, AppClassLoaderthese two loaders are Launcherboth internal classes of and all inherit from URLClassLoader, and URLClassLoaderfinally inherit fromClassLoader

insert image description here
ClassLoaderThere is a property in parentwhich records the relationship between them

insert image description here

So the class loader system is not 继承, but the delegation system.

We can confirm the relationship between class loaders with the following code:

public class LoaderDemo {
    
    
    public static void main(String[] args) {
    
    

        // 获取系统默认的类加载器:应用类加载器
        ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(appClassLoader);

        // 获取应用类加载器的父类加载器:扩展类加载器
        ClassLoader extClassLoader = appClassLoader.getParent();
        System.out.println(extClassLoader);

        // 获取扩展类加载器的父类加载器:引导类加载器
        ClassLoader bootstrapClassLoader = extClassLoader.getParent();
        System.out.println(bootstrapClassLoader);
    }
}

insert image description here

Because bootstrapClassLoaderin the JVM, and the JVM is written in C++, this class cannot be obtained in the Java environment, so it will be displayed as null.


How to get the class loader?

If you want to get the class loader that loads a class, you can get it through the bytecode object ( XXX.class) .getClassLoader()method of the class

For example:

public class LoaderDemo {
    
    
    public static void main(String[] args) {
    
    

        System.out.println(String.class.getClassLoader());
        System.out.println(DNSNameService.class.getClassLoader().getClass().getName());
        System.out.println(LoaderDemo.class.getClassLoader().getClass().getName());

    }
}

insert image description here


5. Parental delegation model

insert image description here
The parent delegation model means that when loadClassthe method to load the class, the class loader will first request its parent class loader to load, and then recurse in turn. If all parent class loaders fail to load, the current class loader performs the loading operation by itself.

Simply put, it checks whether the loading is successful from the bottom up, and tries to load from the top down.

We ClassLoadercan loadClasslearn about the implementation of parental delegation through the method of the class. The source code is as follows:

    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 {
    
    
                	// 检查该类的父级加载器是否不为 null
                    if (parent != null) {
    
    
                    	// 调父级加载器的 loadClass 方法
                    	// 假如当前类加载器为 AppClassLoader,那么它就会去调 ExtClassLoader 的 loadClass 方法,而 AppClassLoader 和 ExtClassLoader 实际上都是调用了 ClassLoader 中的 loadClass 方法
                        c = parent.loadClass(name, false);
                    } else {
    
    
                    	// 这里就会到 C++ 写的类加载器来加载类
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
    
    
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
    
    
                	// 如果 c 为 null,则表明 bootstrap 类加载器也没有加载成功
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    // 查找类,这里实际上是调了 URLClassLoader 类中的 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;
        }
    }


    /**
     * Returns a class loaded by the bootstrap class loader;
     * or return null if not found.
     */
    private Class<?> findBootstrapClassOrNull(String name)
    {
    
    
        if (!checkName(name)) return null;

        return findBootstrapClass(name);
    }

URLClassLoaderfindClassThe method source code of the class :

    protected Class<?> findClass(final String name)
         throws ClassNotFoundException
    {
    
    
        try {
    
    
            return AccessController.doPrivileged(
                new PrivilegedExceptionAction<Class<?>>() {
    
    
                    public Class<?> run() throws ClassNotFoundException {
    
    
                        String path = name.replace('.', '/').concat(".class");
                        Resource res = ucp.getResource(path, false);
                        if (res != null) {
    
    
                            try {
    
    
                                return defineClass(name, res);
                            } catch (IOException e) {
    
    
                                throw new ClassNotFoundException(name, e);
                            }
                        } else {
    
    
                            throw new ClassNotFoundException(name);
                        }
                    }
                }, acc);
        } catch (java.security.PrivilegedActionException pae) {
    
    
            throw (ClassNotFoundException) pae.getException();
        }
    }

Why use the parent delegation model?

  1. The Java class has a hierarchical relationship with priority along with its class loader. Through this hierarchical relationship, 避免类的重复加载when the parent class loader has already loaded the class, there is no need to load it again with the ClassLoader.
  2. 考虑到安全因素, the types defined in the java core API will not be replaced at will, so that the core API library can be prevented from being tampered with at will.
  3. 保证类的唯一性

Can the parental delegation model be broken?

可以, the parent delegation model is not a mandatory constraint model, but a suggested class loader implementation. If you want to break this model, you need to customize a class loader and rewrite loadClassthe methods so that it does not perform parental delegation.


When is it necessary to break this parental delegation model?

In JDBC, classes from third-party vendors (such as commonly used databases) need to be loaded during the loading process of the core class library rt.jar, and the application class loader is directly specified to load these classes. You need to break the parental delegation model.

The web container class loader in Tomcat also breaks the parental delegation model. In addition to being included in the core class library, the custom WebApplicationClassLoader always loads Classes under its own path first, which is conducive to isolation and safe hot deployment.

Six, custom class loader

Custom loaders require:

  • inheritClassLoader
  • override findClass()method or loadClass()method
import java.io.IOException;
import java.io.InputStream;

public class MyClassLoader extends ClassLoader {
    
    

    /**
     * 如果重写 loadClass 方法则会打破双亲委派
     */
//    @Override
//    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    
    
//        // todo ...
//        return null;
//    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
    
    
        try {
    
    
            String fileName = name.substring(name.lastIndexOf(".")+1)+".class";
            InputStream inputStream = getClass().getResourceAsStream(fileName);
            if (inputStream == null) {
    
    
                throw new ClassNotFoundException(name);
            }
            byte[] b = new byte[inputStream.available()];
            int read = inputStream.read(b);
            return defineClass(name,b,0, b.length);
        } catch (IOException e) {
    
    
            throw new ClassNotFoundException(name);
        }
    }
}

Reference blog:
JVM class loading mechanism: https://blog.csdn.net/weixin_41812379/article/details/124107027

Guess you like

Origin blog.csdn.net/xhmico/article/details/130127161
Recommended