This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

Writing in the front

ClassLoader has been covered in the JVM series: Classloading mechanisms, and this article will continue to explore ClassLoader in more depth from a source point of view.

ClassLoader is an abstract ClassLoader object that loads classes. Given the “binary name” of a class, a classloader attempts to locate or generate a piece of data that constitutes a defined class. “Binary name” : Any class name supplied as a string argument to a ClassLoader method. The string name must be a binary name defined by the Java language specification, as follows:

"java.lang.String"
"java.net.URLClassLoader$3$1"
"java.security.KeyStore$Builder$FileBuilder$1"
Copy the code

The class diagram looks like this:

Launcher core class:

The Launcher class

public class Launcher { private static URLStreamHandlerFactory factory = new Launcher.Factory(); Private static Launcher Launcher = newLauncher(); private static Launcher Launcher = newLauncher(); private static String bootClassPath = System.getProperty("sun.boot.class.path"); private ClassLoader loader; private static URLStreamHandler fileHandler; public static Launcher getLauncher() { return launcher; } // The constructor executes public Launcher() {Launcher.ExtClassLoader var1; Try {/ / initializes the extension class loader var1 = the Launcher. ExtClassLoader. GetExtClassLoader (); } catch (IOException var10) { throw new InternalError("Couldnotcreateextension class loader", var10); } try {/ / initializes the application class loader this. Loader. = the Launcher AppClassLoader. GetAppClassLoader (var1); } catch (IOException var9) { throw new InternalError("Could not create application class loader", var9); } // setContextClassLoader to thread.currentthread ().setcontextclassloader (this.loader); String var2 = System.getProperty("java.security.manager"); if (var2 ! = null) { SecurityManager var3 = null; if (!" ".equals(var2) && !" default".equals(var2)) { try { var3 = (SecurityManager) this.loader.loadClass(var2).newInstance(); } catch (IllegalAccessException var5) { } catch (InstantiationException var6) { } catch (ClassNotFoundException var7) { } catch (ClassCastException var8) { } } else { var3 = new SecurityManager(); } if (var3 == null) { throw new InternalError("Could not create SecurityManager:" + var2); } System.setSecurityManager(var3); }}Copy the code

Here the constructor Launcher() does four things:

Create an extension class loader

Create the application class loader

Set the ContextClassLoader

If you need to install security Manager

The launcher is static, so when you initialize it, you create an object, that is, the constructor is triggered, so the above four steps are performed. Moving on to the key steps in creating an ExtClassLoader:

static class ExtClassLoader extends URLClassLoader {
    public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
        final File[] var0 = getExtDirs();
        try {
            return (Launcher.ExtClassLoader) AccessController.doPrivileged(new PrivilegedExceptionAction<Launcher.ExtClassLoader>() {
                public Launcher.ExtClassLoader run() throws IOException {
                    int var1 = var0.length;
                    for (int var2 = 0; var2 < var1; ++var2) {
                        MetaIndex.registerDirectory(var0[var2]);
                    }
                    return new Launcher.ExtClassLoader(var0);
                }
            });
        } catch (PrivilegedActionException var2) {
            throw (IOException) var2.getException();
        }
    }

    void addExtURL(URL var1) {
        super.addURL(var1);
    }

    public ExtClassLoader(File[] var1) throws IOException {
        super(getExtURLs(var1), (ClassLoader) null, Launcher.factory);
        SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
    }

    private static File[] getExtDirs() {
        String var0 = System.getProperty("java.ext.dirs");
        File[] var1;
        if (var0 != null) {
            StringTokenizer var2 = new StringTokenizer(var0, File.pathSeparator);
            int var3 = var2.countTokens();
            var1 = new File[var3];
            for (int var4 = 0; var4 < var3; ++var4) {
                var1[var4] = new File(var2.nextToken());
            }
        } else {
            var1 = new File[0];
        }
        return var1;
    }
}
Copy the code

There are also key steps in creating an AppClassLoader:

Public Class<? > loadClass(String var1, boolean var2) throws ClassNotFoundException { int var3 = var1.lastIndexOf(46); if (var3 ! = -1) { SecurityManager var4 = System.getSecurityManager(); if (var4 ! = null) { var4.checkPackageAccess(var1.substring(0, var3)); }} // Is usually false, and to return TRUE you may need to set the startup parameter lookupCacheEnabled to TRUE. If (this.ucp. KnownToNotExist (var1)) {// Return the class if it has already been loaded by the classloader, Otherwise return Null Class var5 = this.findLoadedClass(var1); if (var5 ! If (var2) {this.resolveclass (var5); // If (var2) {this.resolveclass (var5); } return var5; } else { throw new ClassNotFoundException(var1); } } else { return super.loadClass(var1, var2); }}Copy the code

ClassLoader source code analysis

ClassLoader class, which is an abstract class, all subsequent class loaders inherit from ClassLoader (excluding the start ClassLoader), here mainly introduces several more important methods in ClassLoader.

loadClass

This method loads binary types with specified names (including package names). This method is not recommended to be overridden after JDK1.2, but users can call this method directly. The loadClass() method is implemented by the ClassLoader class itself. LoadClass (String name, Boolean resolve) loadClass(String name, Boolean resolve) loadClass(String name, Boolean resolve) loadClass(String name, Boolean resolve)

Here’s how to load a class again:

1. Then the APP will first check whether A has been loaded, if so, it will return directly;

2, if not, go to ext to check whether A has been loaded, if so, directly return;

3. If no, go to boot to check whether A has been loaded, if so, directly return;

If E:\Java\jdk1.8\jre\lib*. Jar = E:\Java\jdk1.8\jre\lib*. Jar = E:\Java\jdk1.8\jre\lib*. Jar = E:\Java\jdk1.8\jre\lib*.

5. If no, boot loading fails.

If E:\Java\jdk1.6\jre\lib\ext*. Jar = E:\Java\jdk1.6\jre\lib\ext*. Jar = E:\Java\jdk1.6\jre\lib\ext*.

7, If not found, ext load failure;

8, app load, if the specified name is found in the classpath, load, end;

If not found, throw a ClassNotFoundException

protected Class<? > loadClass(String name, Boolean resolve) throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) {Boolean resolve) throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) { Do you need to reload Class<? > c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent ! C = parent.loadClass(name, false); } else {// If there is no parent class, delegate to the bootloader to load c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent If (c == null) {// if still not found, then invoke findClass in order 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; }}Copy the code

The default implementation of this method, which loads classes with the specified binary name, looks for classes in the following order: Call the findLoadedClass(String) method to check if the class has been loaded

The loadClass(String) method is called with the parent loader. If the parent loader is Null, the class loader loads the virtual machine’s built-in loader calls

The findClass(String) method is used to load the Class. If the Class is found successfully, and the resolve argument is true, the resolveClass(Class) method is called to process the Class.

defineClass

The defineClass() method is used to parse the byte stream into a Class object that the JVM can recognize. This method can instantiate Class objects not only from a Class file, but also in other ways. The defineClass() method is usually used together with the findClass() method. In general, when a custom ClassLoader is defined, the findClass() method of the ClassLoader is overridden directly and the loading rules are written. The bytecode of the class to be loaded is obtained and converted into a stream. Then call defineClass() to generate the Class object, as shown in a simple example:

protected Class<? > findClass(String name) throws ClassNotFoundException {byte[] classData = getClassData(name); if (classData == null) { throw new ClassNotFoundException(); } else {return defineClass(name, classData, 0, classdata.length); }}Copy the code

However, if you call defineClass() directly to generate a Class object, the Class object is not resolved (it is still in the link phase, so the link needs to be resolved) and the resolution will have to wait for the initialization phase.

findClass(String)

As you can see from the previous analysis, the findClass() method is called in the loadClass() method. When the parent loader of the loadClass() method fails to load, it calls its own findClass() method to complete the class loading. This ensures that the custom class loader also complies with the parent delegate pattern. Note that there is no specific code logic in the ClassLoader class that implements the findClass() method. Instead, it throws a ClassNotFoundException, It should also be noted that the findClass() method is usually used with the defineClass method in the ClassLoader class.

// Throw the exception protected Class<? > findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); }Copy the code

resolveClass

The resolveClass method can be created using the Class object and resolved at the same time. The link stage is mainly used to validate the bytecode, allocate memory for class variables and set initial values, and convert symbolic references in the bytecode file to direct references.

conclusion

Read a lot of big guy to write class loader article, but their summary write once, look at the source code harvest is real.

Draw that

1. This event is supported by the Nuggets official details can be found at juejin.cn/post/701221…

2. You can participate through comments and articles related content, and article content related oh!

3. This month’s articles will participate in the lucky draw, welcome more interaction!

4. In addition to the official lottery of nuggets, I will also give peripheral gifts (one mug and several nuggets badges, mugs will be given to fans, badges will be randomly selected, the number of badges will increase according to the number of comments).