The process by which class loading is run

In Windows, for example, when the program starts, the main java.exe program calls the underlying JVM.dll file to create a virtual machine and create a boot class loader

The loadClass step

  • Load: Only used classes are loaded, generating a Class object as an access point to the method area.
  • Verification: verify the correctness of class file format and content.
  • Preparation: Allocates memory for static variables of the loaded class and assigns default values.
  • Parse: Replace symbolic references with direct references.
  • Initialization: Initializes the specified value for the static variable of the class and executes the static code block.

Class loader

The process of class loading is implemented through the class loader.

Class loader types:

  • BootstrapClassLoader: loads core class libraries in the lib directory of the JRE, such as rt.jar
  • ExtClassLoader: loads the extended class library in the Ext directory in the lib directory of the JRE.
  • AppClassLoader: Is responsible for loading classes from the classpath path, referencing jar packages, or its own classes.
  • Custom loader: Responsible for the custom path under the class package.

Output class loader

System.out.println(String.class.getClassLoader()); // the reference loader is created by C++, so Java cannot get it directly
System.out.println(DESKeyFactory.class.getClassLoader());
System.out.println(TestJDKClassLoader.class.getClassLoader());

ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
ClassLoader extClassLoader = appClassLoader.getParent();
ClassLoader bootstrapLoader = extClassLoader.getParent();
System.out.println("bootStrapLoader : " + bootstrapLoader);
System.out.println("extClassLoader : " + extClassLoader);
System.out.println("appClassLoader : " + appClassLoader);

// Output the result
null
sun.misc.Launcher$ExtClassLoader@404b9385
sun.misc.Launcher$AppClassLoader@18b4aac2
bootStrapLoader : null
extClassLoader : sun.misc.Launcher$ExtClassLoader@404b9385
appClassLoader : sun.misc.Launcher$AppClassLoader@18b4aac2
Copy the code

Because String is loaded by the bootstrap classloader, which is implemented by C++ low-level, the output is null.

BootstrapLoader similarly.

Class load initialization

A JVM Launcher is created when the JVM is created underneath Java, with an example of Sun.misc.Launcher

The Launcher initialization uses singleton mode

The Launcher call constructor creates two class loaders, ExtClassLoader and AppClassLoader, and returns the created loader by default by calling the getClassLoader() method.

Why AppClassLoader?

Do not understand the source code!

The Launcher call to getClassLoader directly returns the loader property.

When the Launcher constructor is run, the Launcher first creates an ExtClassLoader(an extended class loader), then creates an AppClassLoader and passes the generated ExtClassLoader into the AppClassLoader creation method argument. And returns the generated AppClassLoader to this.loader

AppClassLoader returns this.loader

So what does ExtClassLoader do when it passes in to getAppClassLoader?

Continue to look at the source!

You end up just initializing an AppClassLoader

So what does the parent URLClassLoader do

This parameter is named parent and keeps going.

The parent class is ExtClassLoader, and the parent class is ExtClassLoader.

So there is a parent-child relationship between AppClassLoader, ExtClassLoader and BootstrapClassLoader. (Note that the father-son relationship is not an inherited one!)

So why this father-son relationship? Here comes the big one!


Parent delegation mechanism

When a class is loaded, it will first entrust the parent loader to find the target class, and then continue to entrust the upper parent loader. If all the parent loaders cannot find the target class, they will find and load it in their own class loading path.

Say simple point is to have seek father in advance!

So how does the JVM implement parental delegation?

No nonsense or direct look at the source code!

The following code is the loadClass method in the AppClassLoader class in the Launcher class

Let’s ignore the previous code and focus on the code in the red box here!

This calls the loadClass() method of the parent directly. Who is the parent? URLClassLoader

Let’s go have a look!

It’s still going up! Finally, it’s back to the ClassLoader class

Here comes the big one!

The final logic of this method is as follows:

  1. First check whether the specified class has been loaded, if loaded, do not need to return the class directly.
  2. If not, check if there is a parent loader, and if there is a parent loader
  3. If neither the parent nor the Bootstrap loader finds the specified class, the findClass method of the current class loader is called to load it.
// This is the loadClass method in the ClassLoader class
protectedClass<? > loadClass(String name,boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            // First, check whether the current class loader has already loaded the class and call the underlying native methodClass<? > c = findLoadedClass(name);if (c == null) {// The current class has not been loaded
                long t0 = System.nanoTime();
                try {
                	// If there is a parent loader, call the parent loadClass method (ClassLoader)
                	// The actual parent loader is still a ClassLoader class, and the method is called recursively!
                    if(parent ! =null) {
                        c = parent.loadClass(name, false);
                    } else {
                    	// Search the bootloader for the class. Null is returned if the class is not foundc = findBootstrapClassOrNull(name); }}catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

				// If the class is not found in either the parent or the boot class loader, call its own findClass method to load it
                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the statssun.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

What does parental delegation do?

  • Sandbox security: Prevents core API libraries from being tampered with

  • Avoid class reloading: when the parent loader loads the class, there is no need for the child loader to load the class again, to ensure the uniqueness of the loaded class!

    Look at an example

    package java.lang;
    
    public class String {
        public static void main(String[] args) {
            System.out.println("Custom String class"); }}// Program error!Error: Main method not found in java.lang.String class. Define main as:public static void main(String[] args)Otherwise deployment headaches the application class must extend deployment headaches. Application. The applicationCopy the code

    Because the String class is loaded by the BootstrapClassLoader, the core library does not have a main method for String

Custom class loading

This class has two core methods: loadClass(), which implements the parent delegate mechanism, and findClass, which implements the parent delegate mechanism. The default implementation is an empty method, so custom class loading mainly overrides the findClass method!

public class MyClassLoaderTest {
    static class MyClassLoader extends ClassLoader {
        private String classPath;// Specify the disk location to be read by the class loader

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

        // Write the file to the byte array
        private byte[] loadByte(String name) throws Exception {
            name = name.replaceAll("\ \."."/");
            FileInputStream fileInputStream = new FileInputStream(classPath + "/" + name + ".class");
            int len = fileInputStream.available();
            byte[] data = new byte[len];
            fileInputStream.read(data);
            fileInputStream.close();
            return data;
        }

        // Override the findClass method
        protectedClass<? > findClass(String name)throws ClassNotFoundException {
            try {
                byte[] data = loadByte(name);// Get the class's byte array
                return defineClass(name, data, 0, data.length);// Convert a byte array to a Class object
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null; }}public static void main(String[] args) throws Exception {
        MyClassLoader myClassLoader = new MyClassLoader("E:/test"); Class<? > aClass = myClassLoader.loadClass("com.wf.User"); System.out.println(aClass.getClassLoader()); }}// Final execution result
sun.misc.Launcher$AppClassLoader@18b4aac2
Copy the code

Ever wonder why the User class is still loaded by AppClassLoader after we use our own MyClassLoader?

If you still don’t understand this, then you don’t really understand parental delegation!

First, the custom loader will find the parent loader, AppClassLoader, and then continue to look up, and finally load by AppClassLoader!

What if we want our own MyClassLoader to load?

Simply delete the User class from the project and look at the class file under Target

Done!

Break the parent delegate mechanism

The parent delegate mechanism is implemented in the loadClass method of the ClassLoader class, so it is easy to break this rule. Just override the loadClass method.

Get rid of the core code for the parent delegate mechanism

protectedClass<? > loadClass(String name,boolean resolve)
                throws ClassNotFoundException
        {
            synchronized (getClassLoadingLock(name)) {
                // First, check if the class has already been loadedClass<? > c = findLoadedClass(name);if (c == null) {
                    long t0 = System.nanoTime();
					/*try { if (parent ! = null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader }*/
                    if (c == null) {
                        // If still not found, then invoke findClass in order
                        // to find the class.
                        long t1 = System.nanoTime();
                        c = findClass(name);

                        // this is the defining class loader; record the statssun.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

Done! Run the tests!

What? Error reported??

Java. IO. FileNotFoundException: E: \ test \ Java \ lang \ Object class (system can not find the specified path. at java.io.FileInputStream.open0(Native Method) at java.io.FileInputStream.open(FileInputStream.java:195)
	at java.io.FileInputStream.<init>(FileInputStream.java:138)
	at java.io.FileInputStream.<init>(FileInputStream.java:93)
	at com.wf.jvm.MyClassLoaderTest$MyClassLoader.loadByte(MyClassLoaderTest.java:15)
	at com.wf.jvm.MyClassLoaderTest$MyClassLoader.findClass(MyClassLoaderTest.java:25)
	at com.wf.jvm.MyClassLoaderTest$MyClassLoader.loadClass(MyClassLoaderTest.java:45)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
	at com.wf.jvm.MyClassLoaderTest$MyClassLoader.findClass(MyClassLoaderTest.java:26)
	at com.wf.jvm.MyClassLoaderTest$MyClassLoader.loadClass(MyClassLoaderTest.java:45)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at com.wf.jvm.MyClassLoaderTest.main(MyClassLoaderTest.java:64)
Exception in thread "main" java.lang.NoClassDefFoundError: java/lang/Object
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
	at com.wf.jvm.MyClassLoaderTest$MyClassLoader.findClass(MyClassLoaderTest.java:26)
	at com.wf.jvm.MyClassLoaderTest$MyClassLoader.loadClass(MyClassLoaderTest.java:45)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at com.wf.jvm.MyClassLoaderTest.main(MyClassLoaderTest.java:64)

Process finished with exit code 1

Copy the code

It doesn’t matter! If it’s wrong, we find the problem!

FileNotFoundException failed to find object. class file!

Why did this happen? Because every class has a superclass called Object

Our loader only loads the User class, and our parent loader is masked so we can’t load the Object class

protectedClass<? > loadClass(String name,boolean resolve)
                throws ClassNotFoundException
        {
            synchronized (getClassLoadingLock(name)) {
                // First, check if the class has already been loadedClass<? > c = findLoadedClass(name);if (c == null) {
                    long t0 = System.nanoTime();
                    // All paths that are not loaded by the custom loader are loaded by the parent loader
                    if(! name.startsWith("com.wf")){
                        c = super.loadClass(name, resolve);
                    }else{
                        if (c == null) {
                            // If still not found, then invoke findClass in order
                            // to find the class.
                            long t1 = System.nanoTime();
                            c = findClass(name);

                            // this is the defining class loader; record the statssun.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

Problem solved, the project User still use our custom loader to load the disk directory we specified class files!

Why break the parental delegation mechanism?

For example, Tomcat is a Web container, but multiple applications may need to be deployed. Different applications may rely on different versions of the same third-party class library. Therefore, ensure that the class libraries of each application are independent and isolated from each other