Author someone Valar if need to reprint please keep the original link

The Java source code involved in this article is Java8 version

Some pictures from Baidu, if there is infringement please contact delete

Directory:

  • Class loader
  • Java. Lang. This class
    • With SecureClassLoader URLClassLoader
    • ClassLoader common method source code analysis
  • Parental delegation mechanism
    • The illustration
    • Source code analysis
  • Analysis of common problems

Introduction: When we were first introduced to Java, all we wrote in IDE (Integrated Development Environment) or text editor were.java files, which were compiled to produce.class files, also known as bytecode files.

javac HelloWorld.java   --->  HelloWorld.class
Copy the code

For.class files, they need to be loaded into the virtual machine before they can be used. This process is called class loading. If you want to understand how classes are loaded, you need to know the concepts of class loaders and the parent delegate mechanism. That’s what we’re going to cover here.

1. Class loader

Class loaders in Java fall into two categories:

  • System class loader
  • Custom class loaders

The system class loader has three:

  • Bootstrap ClassLoader: starts the ClassLoader
  • Extensions ClassLoader: Extensions ClassLoader
  • App ClassLoader: also called SystemAppClass, system ClassLoader

1.1 the Bootstrap this

The Bootstrap ClassLoader is used to load system classes required by the JVM(Java virtual machine). The Bootstrap ClassLoader is implemented in c++.

Load the class from the following path:

  1. %JAVA_HOME%/jre/libDirectories such as rt.jar, resources.jar, charsets.jar, etc
  2. You can change the loading directory of the Bootstrap ClassLoader by specifying the -xBootclasspath parameter at JVM startup.

The Java virtual machine is started by creating an initial class by Bootstrap ClassLoader. You can use the following code to calculate the directory loaded by Bootstrap ClassLoader:

public class ClassLoaderTest {
    public static void main(String[]args) {
        System.out.println(System.getProperty("sun.boot.class.path")); }}Copy the code

The printed result is:

C: \ Program Files \ Java \ jdk1.8.0 _102 \ jre \ lib \ resources jar; C: \ Program Files \ Java \ jdk1.8.0 _102 \ jre \ lib \ rt jar; C: \ Program Files \ Java \ jdk1.8.0 _102 \ jre \ lib \ sunrsasign jar; C: \ Program Files \ Java \ jdk1.8.0 _102 \ jre \ lib \ jsse jar; C: \ Program Files \ Java \ jdk1.8.0 _102 \ jre \ lib \ jce jar; C: \ Program Files \ Java \ jdk1.8.0 _102 \ jre \ lib \ charsets jar; C: \ Program Files \ Java \ jdk1.8.0 _102 \ jre \ lib \ JFR jar; C: \ Program Files \ Java \ jdk1.8.0 _102 \ jre \ classesCopy the code

You can find almost all jars in $JAVA_HOME/jre/lib, including rt.jar, resources.jar, and charsets.jar.

1.2 Extensions of this

The Extensions ClassLoader is implemented by the ExtClassLoader class, which is a static inner class of the Sun.misc.Launcher class. The Launcher class can be thought of as an entry point to the Java VIRTUAL machine.

Part of the code for ExtClassLoader is as follows:

The Extensions ClassLoader is responsible for loading into memory the JAVA_HOME/jre/lib/ext or the class libraries in the location specified by the system variable -djava.ext. dir.

To get the Extensions ClassLoader load directory, use the following code:

System.out.println(System.getProperty("java.ext.dirs"));
Copy the code

The printed result is:

C: \ Program Files \ Java \ jdk1.8.0 _102 \ jre \ lib \ ext. C:\Windows\Sun\Java\lib\extCopy the code

1.3 this App

Also known as SystemAppClass, it is implemented by the AppClassLoader class, which is also located in the Sun.misc.Launcher class.

Part of the code is as follows:

  1. It loads all jar and Class files in the Classpath directory and is the default Class loader in the program. Classpath here refers to the bin directory of our Java project.
  2. You can also load JAR and class files in the directory specified by the -djava.class. path option.

To get the App ClassLoader loading directory, run the following code:

System.out.println(System.getProperty("java.class.path"));
Copy the code

The printed result is:

C:\workspace\Demo\bin
Copy the code

This path is actually the current Java project directory bin, which holds the class files generated by compilation.


In Java, in addition to the three system-provided classloaders mentioned above, you can also customize a class loader.

1.4. Custom class loaders

To load jar packages or class files from the specified directory, we can implement our own class loader by inheriting the java.lang.ClassLoader class.

When customizing a classloader, we typically override the findClass method and call the defineClass method within the findClass method.

I’ll start with a look at ClassLoader classes and then look at a custom ClassLoader demo.

Java 2. Lang. This class

2.1 Relationship between ClassLoader, URLClassLoader, and SecureClassLoader

ExtClassLoader (ExtClassLoader), AppClassLoader (ExtClassLoader), URLClassLoader (ExtClassLoader), URLClassLoader (ExtClassLoader), URLClassLoader (ExtClassLoader)

Let’s start with an important inheritance diagram:

  • ClassLoader is an abstract class, located under the java.lang package, that defines the main functions of ClassLoader.
  • SecureClassLoader inherits the abstract class ClassLoader, but SecureClassLoader is not the implementation of ClassLoader. SecureClassLoader extends the ClassLoader class and adds permissions to enhance ClassLoader security.
  • URLClassLoader inherits from SecureClassLoader and is used to load classes and resources from JAR files and folders through URl paths.
  • ExtClassLoader and AppClassLoader both inherit from URLClassLoader. They are both internal classes of Launcher, which is an entry application to the Java VIRTUAL machine. Both ExtClassLoader and AppClassLoader are initialized in the Launcher.

2.2 Relationship between Common Classes, AppClassLoader, and ExtClassLoader

Relationship:

  • To load a normal class, the loader is AppClassLoader, and the parent of the AppClassLoader is ExtClassLoader
  • The parent loader of ExtClassLoader is Bottstrap ClassLoader

Two more conclusions:

  • Every class has a classloader
  • Every class loader has a parent loader

Let’s prepare a simple demo with a test.java file.

public class Test{}
Copy the code
public class Main {
    public static void main(String[] args) {
		ClassLoader cl = Test.class.getClassLoader();
		System.out.println("ClassLoader is:"+cl.toString()); }}Copy the code

This will get the classloader for the test.class file and print it out. The result is:

sun.misc.Launcher$AppClassLoader@75b83e92
Copy the code

The test. class file is loaded by the AppClassLoader.

So who loads the AppClassLoader? AppClassLoader also has a parent loader, which can be obtained with the following code

public class Test {
    public static void main(String[] args) {
        ClassLoader loader = Test.class.getClassLoader();
        while(loader ! =null) { System.out.println(loader); loader = loader.getParent(); }}}Copy the code

The above code results in the following:

sun.misc.Launcher$AppClassLoader@7565783b
sun.misc.Launcher$ExtClassLoader@1b586d23
Copy the code
  • The class loader used to load Test is AppClassLoader, and the parent of AppClassLoader is ExtClassLoader
  • The parent loader of ExtClassLoader is Bottstrap ClassLoader

Bootstrap ClassLoader, the parent of ExtClassLoader, is not printed because Bootstrap ClassLoader is written in C++ and is not a Java class, so we cannot get a reference to it in Java code.

2.3 Common methods of the java.lang.ClassLoader class

In the previous section we saw that the getParent method of the ClassLoader gets the parent loader. This section introduces some of the important methods in ClassLoader, using the source code.

getParent()
This class -- -- -- -- -- -- -- -- --public final ClassLoader getParent(a) {
    if (parent == null) return null;
    SecurityManager sm = System.getSecurityManager();
    if(sm ! =null) {
        checkClassLoaderPermission(parent, Reflection.getCallerClass());
    }
    return parent;
}
Copy the code

As you can see, the return value can be null or a parent variable.

GetParent () ¶ getParent() ¶ getParent() ¶ getParent() ¶ getParent() ¶ getParent() ¶ getParent() ¶

What is the parent variable? When we look at the source code, we can see that the parent assignment is in the constructor.

This class -- -- -- -- -- -- -- -- --private ClassLoader(Void unused, ClassLoader parent) {
    this.parent = parent; .// omit extraneous code
}
Copy the code

This constructor is private and cannot be called externally, so its caller is still inside. Two more constructors were then found.

This class -- -- -- -- -- -- -- -- --protected ClassLoader(a) {
    this(checkCreateClassLoader(), getSystemClassLoader());
}
    
protected ClassLoader(ClassLoader parent) {
    this(checkCreateClassLoader(), parent);
}
Copy the code

So:

  1. You can specify a parent when the ClassLoder constructor is called.
  2. If not specified, it will be usedgetSystemClassLoader()Method return value.

GetSystemClassLoader ();

This class -- -- -- -- -- -- -- -- --public static ClassLoader getSystemClassLoader(a) {
    initSystemClassLoader();
    if (scl == null) {
        return null;
    }
    SecurityManager sm = System.getSecurityManager();
    if(sm ! =null) {
        checkClassLoaderPermission(scl, Reflection.getCallerClass());
    }
    return scl;
}
Copy the code

It returns an SCL. Assignment to the SCL variable was found in the initSystemClassLoader() method.

This class -- -- -- -- -- -- -- -- --private static synchronized void initSystemClassLoader(a) {
    if(! sclSet) {if(scl ! =null)
            throw new IllegalStateException("recursive invocation");
        sun.misc.Launcher l = sun.misc.Launcher.getLauncher(); / / 1
        if(l ! =null) {
            Throwable oops = null; scl = l.getClassLoader(); .// omit the code
        }
        sclSet = true; }}Copy the code

The point is that in comment 1 it gets the object of the Launcher class and calls the getClassLoader() method of the Launcher class.

The Launcher class -- -- -- -- -- -- -- -- --public ClassLoader getClassLoader(a) {
    return this.loader;
}
Copy the code

So what is this.loader? In the Launcher class, the assignment operation is in the constructor of the Launcher, and its value is the AppClassLoader in the Launcher class:

The Launcher class -- -- -- -- -- -- -- -- --public Launcher(a) {
    Launcher.ExtClassLoader var1;
    try {
        var1 = Launcher.ExtClassLoader.getExtClassLoader();
    } catch (IOException var10) {
        throw new InternalError("Could not create extension class loader", var10);
    }

    try {
        this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
    } catch (IOException var9) {
        throw new InternalError("Could not create application class loader", var9); }... }Copy the code

Here the mystery is all solved:

When you create ClassLoder,

  1. You can specify a ClassLoder as its parent, or parent loader.
  2. If not specified, it will be usedgetSystemClassLoader()The return value of the method (i.eLauncherIn the classAppClassLoader) as its parent.
  3. The parent loader can be obtained using the getParent() method.
defineClass()

Can convert class binary contents to class objects, and raise exceptions if they do not meet the requirements, such as ClassFormatError, NoClassDefFoundError.

When customizing a ClassLoader, we typically read a specific file as a byte[] object, and then use this method to convert it to a class object.

This class -- -- -- -- -- -- -- -- --/** * String name: indicates the expected binary file name. * byte[] b: the binary data of this class file * int off: the starting position of the class binary data * int len: the total length of the class binary data */

protected finalClass<? > defineClass(String name,byte[] b, int off, int len)
    throws ClassFormatError
{
    return defineClass(name, b, off, len, null);
}


protected finalClass<? > defineClass(String name,byte[] b, int off, int len, ProtectionDomain protectionDomain)
    throwsClassFormatError { protectionDomain = preDefineClass(name, protectionDomain); String source = defineClassSourceLocation(protectionDomain); Class<? > c = defineClass1(name, b, off, len, protectionDomain, source); postDefineClass(c, protectionDomain);return c;
}
Copy the code
findClass()

The findClass() method is normally called by the loadClass() method to load the named class.

This class -- -- -- -- -- -- -- -- --/** * String name: class file name */
protectedClass<? > findClass(String name)throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
} 
Copy the code

See through the source code ClassLoader class there is no specific logic, but waiting for its subclasses to implement, through the above analysis we know that two system class loaders ExtClassLoader and AppClassLoader inherit from URLClassLoader, Let’s take a look at the specific code in URLClassLoader.

URLClassLoader class -- -- -- -- -- -- -- -- --protectedClass<? > findClass(final String name) throws ClassNotFoundException
{
    finalClass<? > result;try {
        result = AccessController.doPrivileged(
            newPrivilegedExceptionAction<Class<? > > () {publicClass<? > 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 newClassNotFoundException(name, e); }...return result;
}

privateClass<? > defineClass(String name, Resource res)throwsIOException { ... URL url = res.getCodeSourceURL(); . java.nio.ByteBuffer bb = res.getByteBuffer();if(bb ! =null) {...return defineClass(name, bb, cs);
    } else {
        byte[] b = res.getBytes(); .return defineClass(name, b, 0, b.length, cs); }}Copy the code

You can see that it calls defineClass(name, res) after processing the name passed in; In this method, we load files in the appropriate format using res resources and urls, and finally load specific classes using the ClassLoader defineClass method.

loadClass()

In the previous section, we said that findClass() is usually called in loadClass(). What is loadClass()? LoadClass () is an implementation of the parent delegate mechanism, so let’s first introduce the parent delegate mechanism and then analyze loadClass().

3 Introduction to the parent delegation mechanism

3.1 Diagram of parental delegation mechanism

A brief introduction to the parent delegate mechanism: the parent delegate mode is used by Class loaders to find classes (i.e., loadclasses)

  1. First check whether the Class is loaded
  2. If not, the Bootstrap ClassLoader will delegate the search to the parent loader instead of itself, and so on, until the Bootstrap ClassLoader at the top level is delegated
  3. If Bootstrap ClassLoader finds the Class, it returns it directly
  4. If not found, then continue to search down, if not found, will finally be handed over to their own search

  • The red arrow represents the direction of the delegate. If the current class loader does not find the class object from the cache, the parent loader will be asked to do something about it. untilBootstrap ClassLoader.
  • And the black arrow represents the search direction, ifBootstrap ClassLoaderAvailable from the%JAVA_HOME%/jre/lib-xbootCLASspath or -xbootclasspath, return the object directly, otherwiseExtClassLoaderTo look up.
  • ExtClassLoaderfromJAVA_HOME/jre/lib/extor-Djava.ext.dirLook for it in the specified location. If you can’t find it, hand it inAppClassLoader.AppClassLoaderLook it up in the bin directory of the current project
  • If you still can’t find it, we’ll customize itCustomClassLoaderSearch, specific search results, depends on how we implement custom ClassLoaderfindClassMethods.

3.2 Source code analysis of parent delegation mechanism

Let’s look at how the parent delegate mechanism is represented in the source code. LoadClass loadClass loadClass loadClass loadClass loadClass

This class -- -- -- -- -- -- -- -- --protectedClass<? > loadClass(String name,boolean resolve) throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // First, check whether the class is loaded by name. If it is loaded, it will return directlyClass<? > c = findLoadedClass(name);if (c == null) {
            long t0 = System.nanoTime();
            try {
                if(parent ! =null) {
                    // If the current class loader has a parent, the parent loadClass() is called.
                    c = parent.loadClass(name, false);
                } else {
                    Call findBootstrapClassOrNull() if parent of the current classloader is empty.c = findBootstrapClassOrNull(name); }}catch (ClassNotFoundException e) {
            }
        
            if (c == null) {
                // 1. If c is still empty, it means that the parent loader has not found the loaded C, and calls findClass to find the loaded C
                // 2. FindClass throws a ClassNotFoundException if it is not in the specified directory
                // 3. After an exception is thrown, the layer call ends, and the child loader continues with the findClass operation
                longt1 = System.nanoTime(); c = findClass(name); sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); }}if (resolve) {
            resolveClass(c);
        }
        returnc; }}Copy the code

FindBootstrapClassOrNull () : you can see that after checking name, it finally calls a native method, findBootstrapClass(). The findBootstrapClass() method ends up using Bootstrap Classloader to find the class.

This class -- -- -- -- -- -- -- -- --privateClass<? > findBootstrapClassOrNull(String name) {if(! checkName(name))return null;
    return findBootstrapClass(name);
}
    
private nativeClass<? > findBootstrapClass(String name);Copy the code

4 Common Problems

4.1 Why parent delegation mechanism?

  1. Avoid double loading. If the Class has already been loaded once, it does not need to be loaded again and is read directly from the cache first.
  2. For security reasons, if you do not use the parent delegate mode, you can customize a String class to replace the system’s String class, which may cause security risks. If you use the parent delegate mode, the system’s String class will be loaded when the Java VIRTUAL machine starts. There is no way to customize the String class to replace the system’s String class.

4.2 Are classes loaded by different classloaders treated by the JVM as the same class?

Don’t. In Java, we use the package name plus the class name to identify a class. But in the JVM, a class is uniquely identified by its package name + class name and an instance of a ClassLoader, and classes loaded by different class loaders are placed in different namespaces.

In a demo,

  1. Load a custom class with two custom class loaders
  2. Then fetch the Class to java.lang.object.equals (…). Judgment.
public class Main {
    public static void main(String[] args) {
    
        ClassLoaderTest myClassLoader = new ClassLoaderTest("F:\\");
        ClassLoaderTest myClassLoader2 = new ClassLoaderTest("F:\\");
        try {
            Class c = myClassLoader.loadClass("com.example.Hello");
            Class c2 = myClassLoader.loadClass("com.example.Hello");

            Class c3 = myClassLoader2.loadClass("com.example.Hello");

            System.out.println(c.equals(c2)); //true
            System.out.println(c.equals(c3)); //flase}}Copy the code

Output result:

true
false
Copy the code

The Java virtual machine considers two classes to be the same class only if they have the same class name and are loaded by the same classloader.

Custom ClassLoader used in the demo above:

Custom class loaders note:1Override the findClass method2. To load the file from the path we specified according to name, i.e. to convert the file correctlybyte[] format3. Use the defineClass methodbyte[] Data is converted to a Class object -------------public class ClassLoaderTest extends ClassLoader{
    private String path;
    public ClassLoaderTest(String path) {
        this.path = path;
    }
    @Override
    protectedClass<? > findClass(String name)throws ClassNotFoundException {
        Class clazz = null;
        byte[] classData = classToBytes(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        } else {
            clazz= defineClass(name, classData, 0, classData.length);
        }
        return clazz;
    }
    private byte[] classToBytes(String name) {
        String fileName = getFileName(name);
        File file = new File(path,fileName);
        InputStream in=null;
        ByteArrayOutputStream out=null;
        try {
            in = new FileInputStream(file);
            out = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int length=0;
            while((length = in.read(buffer)) ! = -1) {
                out.write(buffer, 0, length);
            }
            return out.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if(in! =null) { in.close(); }}catch (IOException e) {
                e.printStackTrace();
            }
            try{
                if(out! =null) { out.close(); }}catch(IOException e){ e.printStackTrace(); }}return null;
    }
    private String getFileName(String name) {
        int index = name.lastIndexOf('. ');
        if(index == -1) {return name+".class";
        }else{
            return name.substring(index+1) +".class"; }}}Copy the code

conclusion

Java class loaders and parent delegate mechanisms are all covered in this article. If there are any errors, or there are other important aspects of class loaders that are not covered, feel free to leave a comment in the comments section.

The next article will cover the process of creating an object in Java, which involves loading classes, validating classes, and allocating heap memory during object creation.

Reference: liuwangshu. Cn/application…

Blog.csdn.net/briblue/art…

Blog.csdn.net/justloveyou…