Detailed explanation of JVM_class loading mechanism

Class File Loading Process (Class Loading Process)

Load - > Connect (Verification - > Prepare - > Resolve) - > Initialization - > Use - > Unload

Load

In the loading stage, the jvm obtains such bytecode files (. class files) by the class name.
The data structure in the file is then transferred to memory (converted into data structure in the runtime method area).
Finally, a Class object representing this class is generated in the heap for later users to create objects or call related methods.

Verification

The validation phase is used to ensure that the Class file conforms to the jvm specification, and if the validation fails, an error is thrown.

Get ready

At this stage, virtual opportunity configures memory space for static member variables of class objects and assigns initial values.

analysis

Replace number references in class/interface/field/method with direct references.

Initialization

The virtual opportunity invokes the initialization method of the class object to assign the class variable.

Class File Loading Conditions

There must be a class to actively use the Class.
The ways are as follows:
new keywords, reflection, cloning and deserialization are used.
Call static methods of classes;
When a subclass of a class is called, its parent class is initialized.
A class that contains the main() method.

Passive use does not load Class.
The ways are as follows:
The static method of its parent class is called.

Summary:

jvm adheres to pragmatism and does not load classes that are not used.
But in the startup of java code, some of the classes used are loaded.

Loader type

Bootstrap Class Loader:

jdk8 is used to load the classes required by jvm itself, and c + + is used to load rt.jar.
In JDK after jdk9, Bootstrap ClassLoader is mainly used to load core system classes in java.base.

Extended Class Loader:

jdk8 is used to load classes in the ${JAVA_HOME}/lib/ext directory.
It has been removed from jdk9.

Platform Class Loader:

jdk9 is then used to replace ExtClassLoader's loader and to load non-core module classes in jdk.

Application Class Loader:

Used to load general application classes.

Custom loader:

User-defined classes generally inherit from java.lang.ClassLoader.

Parental Appointment Mechanism

When any ClassLoader attempts to load a class, it will first try to call the related methods of its parent class to load the class. If its parent class cannot load the class, it will be left to the subclass to complete.

The benefit: For any user-defined lassLoader, try to get the jvm's Bootstrap Class Loader to try to load (all custom lassLoaders inherit them). Then it can ensure that JVM classes will be loaded first, which limits the impact of users on JVM system.

Source code

Source exploration uses jdk11, which is slightly different from jdk8.

ClassLoader

ClassLoader is the top-level parent of the class loader, and its core method is the loadClass(...) method:

// ClassLoader.class
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
    // Locking to ensure thread safety
    synchronized (getClassLoadingLock(name)) {
        // Find out if the class has been loaded once, and if it has already been loaded, you don't need to load it again.
        // The core logic of this method is implemented by c++.
        Class<?> c = findLoadedClass(name);
        // Not loaded
        if (c == null) {
            long t0 = System.nanoTime(); // Recording time
            try {
                // Here the mechanism of parental appointment is embodied.
                // If there is a parent loader in the loader, the related method of the parent loader will be called first.
                // If there is no parent loader, call the Bootstrap loader
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    // Calling BootstrapClassLoader, the core logic of this method is implemented in c++.
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {

            }

            // If it is still not loaded, then the parent loader is still unable to load the information.
            // Then you need the specified loader to load it by itself.
            if (c == null) {

                long t1 = System.nanoTime();
                
                // The loader loads the core logic of class files
                // This method is left blank in ClassLoader and needs subclasses to be implemented according to their own logic.
                c = findClass(name);

                // Here are some information records, independent of the main logic
                PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                PerfCounter.getFindClasses().increment();
            }
        }
        
        if (resolve) {
            // Parsing class is also left blank and needs subclasses to implement.
            resolveClass(c);
        }
        return c;
    }
}

BuiltinClassLoader

BuiltinClassLoader is a loader replacing URLClassLoader in jdk9. It is the parent class of Platform ClassLoader and AppClassLoader. It inherits SecureClassLoader and its core method is the loadClassOrNull(...) method:

// BuiltinClassLoader.class

// step 1
@Override
protected Class<?> loadClass(String cn, boolean resolve) throws ClassNotFoundException{
    // Overrides the loadClass(...) method, but the core is to call loadClassOrNull(...)
    Class<?> c = loadClassOrNull(cn, resolve);
    if (c == null)
        throw new ClassNotFoundException(cn);
    return c;
}

// step 2
protected Class<?> loadClassOrNull(String cn, boolean resolve) {
    // Locking to ensure thread safety
    synchronized (getClassLoadingLock(cn)) {
        // First, find out if the class has been loaded. This method is in ClassLoader.
        Class<?> c = findLoadedClass(cn);

        if (c == null) {

            // Here you need to load the module information first.
            LoadedModule loadedModule = findLoadedModule(cn);
            if (loadedModule != null) {
                BuiltinClassLoader loader = loadedModule.loader();
                if (loader == this) {
                    if (VM.isModuleSystemInited()) {
                        c = findClassInModuleOrNull(loadedModule, cn);
                    }
                } else {
                    c = loader.loadClassOrNull(cn);
                }
            } else {

                // Call the parent loader's method first to load it once
                if (parent != null) {
                    c = parent.loadClassOrNull(cn);
                }

                // If not loaded, use the current loader to load
                if (c == null && hasClassPath() && VM.isModuleSystemInited(){
                    // This method calls the defineClass(...) method to load the class file.
                    c = findClassOnClassPathOrNull(cn);
                }
            }

        }

        // Analyzing class
        if (resolve && c != null)
            resolveClass(c);

        return c;
    }
}

There is also a way to load class bytecode in the loader:

// BuiltinClassLoader.class
private Class<?> defineClass(String cn, Resource res) throws IOException{
    
    URL url = res.getCodeSourceURL();

    // First parse the path of the class
    int pos = cn.lastIndexOf('.');
    if (pos != -1) {
        String pn = cn.substring(0, pos);
        Manifest man = res.getManifest();
        defineOrCheckPackage(pn, man, url);
    }

    // Here the class is read out as a byte [] string and loaded through the relevant jvm methods
    ByteBuffer bb = res.getByteBuffer();
    if (bb != null) {
        CodeSigner[] signers = res.getCodeSigners();
        CodeSource cs = new CodeSource(url, signers);
        // This method finally calls the native method in ClassLoader
        return defineClass(cn, bb, cs);
    } else {
        byte[] b = res.getBytes();
        CodeSigner[] signers = res.getCodeSigners();
        CodeSource cs = new CodeSource(url, signers);
        // This method finally calls the native method in ClassLoader
        return defineClass(cn, b, 0, b.length, cs);
    }
}

BootClassLoader

BootClassLoader is a static inner class of ClassLoaders. Although it is a subclass of BuiltinClassLoader in code implementation, it is a parent class of Platform ClassLoader in function.

// ClassLoader.class
private static class BootClassLoader extends BuiltinClassLoader {
    BootClassLoader(URLClassPath bcp) {
        super(null, null, bcp);
    }

    // Override the loadClassOrNull(...) method in BuiltinClassLoader
    @Override
    protected Class<?> loadClassOrNull(String cn) {
        return JLA.findBootstrapClassOrNull(this, cn);
    }
};

PlatformClassLoader

Platform ClassLoader is also a static internal class of ClassLoaders. Functionally, it is a subclass of BootClassLoader and a parent class of AppClassLoader. Platform Class Loader is mainly used to load some module s:

// ClassLoader.class
private static class PlatformClassLoader extends BuiltinClassLoader {
    static {
        if (!ClassLoader.registerAsParallelCapable())
            throw new InternalError();
    }

    // BootClassLoader is passed in here as a parent parameter
    PlatformClassLoader(BootClassLoader parent) {
        super("platform", parent, null);
    }

    // Loading module
    private Package definePackage(String pn, Module module) {
        return JLA.definePackage(this, pn, module);
    }
}

AppClassLoader

The core method of AppClassLoader is loadClass(...), which eventually calls the BuiltinClassLoader.loadClassOrNull(...) method, which in turn calls the Platform ClassLoader. loadClass(...) method internally; then, in fact, the Platform ClassLoader calls the ClassloadNull (...) side of BootClassLoader internally. Law. In this way, the parent-to-parent delegation mechanism of the class loader is completed:

// ClassLoader.class
private static class AppClassLoader extends BuiltinClassLoader {
    static {
        if (!ClassLoader.registerAsParallelCapable())
            throw new InternalError();
    }

    final URLClassPath ucp;

    // Platform ClassLoader is passed in here as a parent parameter
    AppClassLoader(PlatformClassLoader parent, URLClassPath ucp) {
        super("app", parent, ucp);
        this.ucp = ucp;
    }

    @Override
    protected Class<?> loadClass(String cn, boolean resolve) throws ClassNotFoundException{
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            int i = cn.lastIndexOf('.');
            if (i != -1) {
                sm.checkPackageAccess(cn.substring(0, i));
            }
        }
        // The BuiltinClassLoader.loadClassOrNull(...) method is actually called
        return super.loadClass(cn, resolve);
    }

    @Override
    protected PermissionCollection getPermissions(CodeSource cs) {
        PermissionCollection perms = super.getPermissions(cs);
        perms.add(new RuntimePermission("exitVM"));
        return perms;
    }


    void appendToClassPathForInstrumentation(String path) {
        ucp.addFile(path);
    }


    private Package definePackage(String pn, Module module) {
        return JLA.definePackage(this, pn, module);
    }


    protected Package defineOrCheckPackage(String pn, Manifest man, URL url) {
        return super.defineOrCheckPackage(pn, man, url);
    }
}

Keywords: Java jvm JDK

Added by $Three3 on Sun, 18 Aug 2019 18:11:01 +0300