当前位置:网站首页>Class loading mechanism

Class loading mechanism

2022-04-23 19:20:00 Xuanguo Guo

Virtual machine class loading mechanism

A type starts from being loaded into virtual machine memory , Until the memory is unloaded , Its entire life cycle will experience loading 、 verification 、 Get ready 、 analysis 、 initialization 、 Use 、 Uninstall these seven stages . Which verifies 、 Get ready 、 Analysis of three parts called connection .
load 、 verification 、 Get ready 、 The sequence of initialization and uninstallation is determined , The loading process of types must be loaded in this order .

The life cycle of a class :

One 、 Class loading process :

1、 load

Files on hard disk ( Such as class file ) adopt IO Read in bytecode file , It will not load until the class is used , For example, call the main() Method ,new Objects, etc.
  1) Get the binary byte stream that defines this class by using the fully qualified name of a class
  2) Convert the static storage structure represented by the byte stream into the runtime data structure of the method area
  3) Generate a representation of this class in memory java.lang.Class object , As the access entry of each data of this class in the method area

2、 verification

To make sure Class The information contained in the byte stream of the file is consistent with 《Java Virtual machine specification 》 Of all the constraints , Moreover, we must ensure that the information will not endanger the security of the virtual machine after it is running normally .
  1) File format validation : Verify that the byte stream matches Class Specification of document format , And can be processed by the current version of virtual machine .
  2) Metadata validation : Semantic analysis of information described by bytecode , To ensure that the relevant information complies with 《Java Virtual machine specification 》 The requirements of . If this class has a parent class , Whether the parent class of this class is allowed to be inherited, etc
  3) Bytecode verification : The most complicated stage , Through data flow analysis and control flow analysis , Determine whether the program semantics are legal , Logical or not .
  4) Symbol reference validation : Ensure that the parsing behavior can be executed normally . In this stage, the verification is performed when the virtual machine converts the symbolic reference into a direct reference ( When parsing ). Judge whether the class lacks or prohibits access to some external classes it depends on , Method , Fields and other resources .

3、 Get ready

The stage of formally allocating memory for variables defined in the class and setting the initial value of class variables .
public static int a = 1;
After the preparation phase , At this time a It's worth it 0, instead of 1, Because at this time, we haven't started any Java Method , After the class initialization phase a The value of is 1.
After preparing the basic data type , Basically the corresponding zero value .

public static final int b = 2;
After the preparation phase ,b The value of is 2. Why? ?
Because compile time javac Will be b Generate ConstantValue attribute , In the preparation phase, the virtual machine will be based on ConstantValue The settings for will b The assignment is 2.

4、 analysis

yes Java The process of virtual machine replacing symbolic reference in constant pool with direct reference .
Symbol reference : Use a set of symbols to describe the referenced target , A symbol can be any form of literal quantity , As long as it can accurately locate the required target through the symbol when using .
Direct reference : It's a pointer to the target 、 Relative offset 、 Handle that can indirectly locate the target .( Handle :Java Divide an area of memory in the heap as a handle pool ,reference The handle address of this object is stored in , The handle contains the specific address information of the object's instance data and type data .)
  1) Class or interface resolution : When the program executes to a place that has never been parsed , You need to resolve a symbol reference that has never been resolved into a direct reference to a class or interface .
  2) Field analytical : Resolve an unresolved field symbol reference , That is, the symbolic reference of the class or interface to which the field belongs .
  3) Method resolution : Resolve the symbolic reference of the class or interface to which an unresolved method belongs . 
  4) Interface method parsing : Resolve the symbolic reference of the class or interface to which the interface method belongs .

5、 initialization

The last step in the class loading process , The virtual machine really starts executing the program code written in the class .
In the preparation stage , Our variable has actually been assigned once , It's just the initial zero value , In the initialization phase , It will be assigned according to the variables defined in our code .
The initialization phase is the execution of the class constructor <clinit>() Method process .

 

Two 、 Class loader

1、 Class and class loader

For a class , Must be established by its class loader and the class itself Java Uniqueness in virtual machines , Every classloader , All have a separate class namespace .
Compare two classes whether or not " equal ", It makes sense only if the two classes are loaded by the same classloader , Otherwise, even if these two classes are the same Class file , Loaded by the same virtual machine , Just load his classloader differently , Then the two classes must be different .

Code example :

/**
 *  Different class loader pairs instanceof The impact of keyword operation 
 * @Author chenxuan
 */
public class ClassLoadTest {

    /**
     *  We used the class loader to load the class , The results are in progress instanceof The time returned during the operation false
     *  Because there are two in the virtual machine at the same time ClassLoadTest class , One is loaded by the application class of the virtual machine , The other is loaded by our custom class loader .
     *  Although they are from the same class file , But in the virtual machine, there are two independent classes .
     * @Author chenxuan
     **/
    public static void main(String[] args) {
        ClassLoader classLoader = new ClassLoader() {
            @Override
            public Class<?> loadClass(String name) throws ClassNotFoundException {
                String fileName = name.substring(name.lastIndexOf(".")+1)+".class";
                InputStream input = getClass().getResourceAsStream(fileName);
                if(input == null){
                    return super.loadClass(name);
                }
                try {
                    byte[] bytes = new byte[input.available()];
                    input.read(bytes);
                    return defineClass(name,bytes,0,bytes.length);
                } catch (IOException e) {
                    e.printStackTrace();
                }finally {
                    try {
                        input.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                return super.loadClass(name);
            }
        };

        try {
            Class clazz  = classLoader.loadClass("com.example.demo.jvm.classload.ClassLoadTest");
            Object o = clazz.newInstance();
            // Use your own class loader 
            System.out.println(clazz.getClassLoader().getClass().getName());
            System.out.println(o.getClass());
            System.out.println(o instanceof com.example.demo.jvm.classload.ClassLoadTest);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Output results :

com.example.demo.jvm.classload.ClassLoadTest$1
class com.example.demo.jvm.classload.ClassLoadTest
false

2、 Parent delegation model

from Java From the perspective of virtual machine , There are two different class loaders , One is to start the classloader , This class loader is through C++ To achieve ; The other is other class loaders , These class loaders are through Java To achieve , Independent storage and outside the virtual machine , All are inherited from abstract classes java.lang.ClassLoader
 1) Start class loader : Responsible for loading supports JVM The location of the operation is JRE Of lib The core class library under the directory , such as rt.jar、tools.jar etc.
 2) Extend the classloader : In the class sun.misc.Launcher$ExtClassLoader China and Israel Java In the form of code . Responsible for loading supports JVM The location of the operation is JRE Of lib In the catalog ext Expand... In the directory JAR Class package
 3) Application class loader : In the class sun.misc.Launcher$AppClassLoader To achieve . Responsible for loading ClassPath Class package under path , The main thing is to load the classes you write
 4) Custom class loaders : Responsible for loading the class package under the user-defined path

Code example :

/**
 *  Class loader example 
 * @Author chenxuan
 */
public class ClassLoadF {

    public static void main(String[] args) {
        // Start class loader , the reason being that C++ To achieve , So it is null
        System.out.println(String.class.getClassLoader());

        // Extend the classloader 
        System.out.println(com.sun.crypto.provider.DESKeyFactory.class.getClassLoader().getClass().getName());

        // Application class loader 
        System.out.println(ClassLoadF.class.getClassLoader().getClass().getName());

        // Application class loader 
        System.out.println(ClassLoader.getSystemClassLoader().getClass().getName());
    }
}

Output results :

null
sun.misc.Launcher$ExtClassLoader
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$AppClassLoader


The working process of the parent delegation model : If a class loader receives a class load request , He won't load this class immediately , Instead, the loading request is delegated to the parent loader to complete , Then each layer goes up to request , Therefore, all load requests will eventually go to the top-level startup class loader , Only when the parent loader cannot load the class , Will tell the child loader , Make the child loader complete the class loading by itself .

Sandbox security mechanism : His writing java.lang.String.class Class will not be loaded , This prevents the core API The library was tampered with at will
Avoid duplicate loading of classes : When the father has loaded the class , There is no need ClassLoader Load again , Ensure the uniqueness of the loaded class

3、 Break the parental delegation model

Now that we have defined the parental delegation model , So why should we break it ?

With Tomcat Class loading as an example ,Tomcat It's a web Containers , One web The container may need to deploy two applications , Different applications may depend on different versions of the same third-party class library , You can't require the same class library to have only one copy on the same server , So make sure that each application's class library is independent , Make sure they are isolated from each other .
Deployed in the same web The same version of the same class library in the container can be shared . otherwise , If the server has 10 Applications , Then there must be 10 Load the same class library into the virtual machine .
web Containers also have their own dependent class libraries , Can't be confused with an application's class library . Based on safety considerations , The class library of the container should be isolated from the class library of the program .
So it looks like , We tomacat It is not advisable to use the parent delegation class model to load again .
Because the default classloader mechanism is used , So it's impossible to load different versions of two same class libraries , The default class adder is no matter what version you are , Only care about your fully qualified class name , And only one .
tomcat Several main class loaders for :
    commonLoader:Tomcat The most basic classloader , Load... In the path class Can be Tomcat The container itself and the individual Webapp visit ;
    catalinaLoader:Tomcat Container private classloader , Load... In the path class about Webapp invisible ;
    sharedLoader: each Webapp Shared classloader , Load... In the path class For all Webapp so , But for Tomcat The container is not visible ;
    WebappClassLoader: each Webapp Private classloader , Load... In the path class Only for the present Webapp so ;

How to break it ?

Simple code example :

/**
 *  Example of breaking the parental delegation model 
 * @Auther chenxuan
 */
public class ChenClassLoaderTest extends ClassLoader {

    private String classPath;

    public ChenClassLoaderTest(String classPath){
        this.classPath= classPath;
    }

    private byte[] loadByte(String name) throws Exception{

        name = name.replaceAll("\\.","/");
        FileInputStream input = new FileInputStream(classPath+"/"+name+".class");
        int len = input.available();
        byte[] date = new byte[len];
        input.read(date);
        input.close();

        return date;
    }

    /**
     *  rewrite loadClass, Used to break the parental delegation model 
     * @param name
     * @param resolve
     * @return
     * @throws ClassNotFoundException
     */
    @Override
    protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First check whether the class has been loaded 
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                // When the incoming classpath does not meet the conditions , The rules of the parental delegation model are still used 
                if (!name.startsWith("com.xuan.springboot")) {
                    c = this.getParent().loadClass(name);
                }else{
                    long t1 = System.nanoTime();
                    // find class File and load 
                    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;
        }
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException{
        try {
            byte[] date = loadByte(name);
            return defineClass(name,date,0,date.length);
        }catch (Exception e){
            throw new ClassNotFoundException();
        }
    }

    public static void main(String[] args) throws Exception {
        // At this time User Classes are loaded through the application class loader  
        System.out.println(User.class.getClassLoader().getClass().getName());

        // The compiled class Catalog 
        ChenClassLoaderTest classLoader = new ChenClassLoaderTest("d:/myProject/demo/target/classes");
        Class clazz = classLoader.loadClass("com.example.demo.jvm.User");

        Object obj = clazz.newInstance();

        Method method = clazz.getDeclaredMethod("myClassLoader",null);

        method.invoke(obj);

        // Custom class loader loading 
        System.out.println(clazz.getClassLoader().getClass().getName());
       
        System.out.println(obj instanceof User);
    }
}

Results output :

sun.misc.Launcher$AppClassLoader
User  Parameterless constructor 
=====chenxuan ClassLoader method=====
com.example.demo.jvm.ChenClassLoaderTest
false

thus it can be seen , When we load through a custom class loader User.class when , Not according to the rules of the parental delegation model , normal User Is loaded by the application class loader , At this time, it is loaded through our custom class , Therefore, it breaks the parental delegation model .

User class :

public class User {

    private String name;

    public User(){
        System.out.println("User  Parameterless constructor ");
    }
    
    public void myClassLoader(){
        System.out.println("=====chenxuan ClassLoader method=====");
    }
}

Parent delegation model part of the source code :

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    // Lock , Prevent a class from being loaded at the same time 
    synchronized (getClassLoadingLock(name)) {
        // First check whether the class has been loaded 
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                // If the class loader has a parent class loader , Then call... In the parent class loading first loadClass, Recurse all the way up 
                // For example, the current class loader is AppClassLoader, So the parent Namely ExtClassLoader
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    // Recurse until there is no class loader at the top , Then use the startup class loader to load 
                    // If it is not found or cannot be loaded , Then return to null
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            if (c == null) {

                long t1 = System.nanoTime();
                // If the loader of the parent class fails to load the class , Call findClass To find this class 
                // Implemented by different subclass loaders 
                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;
    }
}

/**
 *  Call the startup class loader , You can see that the key is native Local method , It proves that what we said above is caused by C++ Realization 
 *  If you can't find the corresponding class , Then return to null
 */
private Class<?> findBootstrapClassOrNull(String name)
{
    if (!checkName(name)) return null;

    return findBootstrapClass(name);
}

// return null if not found
private native Class<?> findBootstrapClass(String name);

We can know from the source code , Want to break the parental delegation model , Have to rewrite loadClass Method , Reload the custom class where you don't want to call the parent class loader .

版权声明
本文为[Xuanguo Guo]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/04/202204210558503108.html