Simple and easy to understand, ClassLoader

The Java class loader is a component of the Java runtime environment, which is responsible for dynamically loading Java classes into the memory space of the Java virtual machine. Classes are usually loaded on demand, that is, they are loaded when the class is used for the first time. Thanks to the class loader, the Java runtime system does not need to know files and file systems. For learning class loader, it is very important to master the concept of delegation in Java. Every Java class must be loaded into memory by some class loader. (Wikipedia)

Class loader:

There are two types of class loaders: boot class loader provided by java, virtual machine and user-defined class loader, each user-defined class loader is a subclass of ClassLoader

  • Bootstrap (responsible for loading the core class libraries in the JDK, such as: rt.jar, resource.jar, charsets.jar, etc., implemented in C++)
  • Extension (responsible for loading the extended jar package jre/lib/ext/*.jar or specified by -Djava.ext.dirs)
  • App (load classpath specified content)
  • custom (Custom ClassLoader)
public static void main(String[] args) throws ClassNotFoundException {
    
    

        //String java 核心类库 ---> null 由顶级BootStrap加载 C++实现 打印为null=BootStrap
        System.out.println(String.class.getClassLoader());
        //Main 当前的这个类  ---->sun.misc.Launcher$AppClassLoader@18b4aac2是加载classPath路径下的
        System.out.println(Main.class.getClassLoader());
        //获取 AppClassLoader的父加载器--->ExtClassLoader
        System.out.println(Main.class.getClassLoader().getParent());
        //获取 AppClassLoader的父加载器的父加载--->BootStrap ->null
        System.out.println(Main.class.getClassLoader().getParent().getParent());
        
        Class<?> aClass = Class.forName("java.lang.ClassLoader");
        System.out.println(aClass.getClassLoader());
    }
    输出结果:
    null
	sun.misc.Launcher$AppClassLoader@18b4aac2
	sun.misc.Launcher$ExtClassLoader@1b6d3586
	null
	null

Launcher

Launcher is the class used to start the program entry main() in JRE, let us look at the code of Launcher

public class Launcher {
    
    
	 private ClassLoader loader;
	public Launcher() {
    
    
	        Launcher.ExtClassLoader var1;
	        try {
    
    
	        	//加载ExtClassLoader
	            var1 = Launcher.ExtClassLoader.getExtClassLoader();
	        } catch (IOException var10) {
    
    
	            throw new InternalError("Could not create extension class loader", var10);
	        }
	
	        try {
    
    
	        	//加载AppClassLoader
	            this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
	        } catch (IOException var9) {
    
    
	            throw new InternalError("Could not create application class loader", var9);
	        }
        //删除无关代码
	}
}	

Didn’t you see Bootstrap, because it is implemented in c++;
String bootClassPath = System.getProperty("sun.boot.class.path"); You can see the initial loading path
final String var1 = System.getProperty("java.class .path”); //appclassloader loading URL
String var0 = System.getProperty("java.ext.dirs"); //ExtClassLoader loading URL
is now testing which ones are loaded:

public static void main(String[] args){
    
    
 		String pathBoot = System.getProperty("sun.boot.class.path");
        System.out.println(pathBoot.replaceAll(";",System.lineSeparator()));
        
        System.out.println("--------------------");
        String pathExt = System.getProperty("java.ext.dirs");
        System.out.println(pathExt.replaceAll(";", System.lineSeparator()));

        System.out.println("--------------------");
        String pathApp = System.getProperty("java.class.path");
        System.out.println(pathApp.replaceAll(";", System.lineSeparator()));
}
本机输出结果:
"D:\Program Files\Java\jdk1.8.0_261\bin\java.exe"
D:\Program Files\Java\jdk1.8.0_261\jre\lib\resources.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\rt.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\sunrsasign.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\jsse.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\jce.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\charsets.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\jfr.jar
D:\Program Files\Java\jdk1.8.0_261\jre\classes
--------------------
D:\Program Files\Java\jdk1.8.0_261\jre\lib\ext
C:\Windows\Sun\Java\lib\ext
--------------------
D:\Program Files\Java\jdk1.8.0_261\jre\lib\charsets.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\deploy.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\access-bridge-64.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\cldrdata.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\dnsns.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\jaccess.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\jfxrt.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\localedata.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\nashorn.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\sunec.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\sunjce_provider.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\sunmscapi.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\sunpkcs11.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\zipfs.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\javaws.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\jce.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\jfr.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\jfxswt.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\jsse.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\management-agent.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\plugin.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\resources.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\rt.jar
D:\java\code\jvm\out\production\jvm
D:\Program Files\JetBrains\IntelliJ IDEA 2020.2.1\lib\idea_rt.jar

Parent loader

Every class has a parent loader, the first program also saw that it can be obtained through Classloader.getParent(). With particular emphasis here is not the father-son inheritance , but not inherit how you can get to it, the class loader is inherited URLClassLoader this class but this class is useless inside getParent () This method is, in fact, in Classloader source code, you can see:

public abstract class ClassLoader {
    
    
    // The parent class loader for delegation
    // Note: VM hardcoded the offset of this field, thus all new fields
    // must be added *after* it.
    private final ClassLoader parent;
    
    }
    //构造方法
    private ClassLoader(Void unused, ClassLoader parent) {
    
    
        this.parent = parent;
        if (ParallelLoaders.isRegistered(this.getClass())) {
    
    
            parallelLockMap = new ConcurrentHashMap<>();
            package2certs = new ConcurrentHashMap<>();
            assertionLock = new Object();
        } else {
    
    
            // no finer-grained lock; lock on the classloader instance
            parallelLockMap = null;
            package2certs = new Hashtable<>();
            assertionLock = this;
        }
    }

One attribute in Classloader is parent, which clearly specifies the parent loader of the current loader. There are two assignments for parent:

  • Directly specify a ClassLoader as the parent when the ClassLoader is created by the external class in the constructor.
  • Generated by the getSystemClassLoader() method, which is obtained by getClassLoader() in sun.misc.Laucher, which is AppClassLoader. To put it bluntly, if a ClassLoader is created without specifying a parent, then its parent is AppClassLoader by default.
    In the code of Laucher I posted above, it can be seen that the App in Laucher's construction method is copied to the loader, and the loader is the ClassLoader.

So stop talking about inheritance!

Parental delegation

Insert picture description here

It's not too late to post this picture here. Hey, you can compare it to the one above.

Now let’s take a look at how the Class class is loaded, that is, through the process of parental delegation to similar recursive calls, look for layer by layer and then post a picture: Insert picture description here
Now let ’s talk about the loading process, first of all, a .class suffix Look up the class layer by layer , you can see that in the picture above, the CustomClassLoader first goes to find in Cache to find the cache to see if it has been loaded. If it has, it will return the result directly. If not, it will notify the parent loader. Go to find in cache and return the result if there is no, continue with the previous operation. Go to the Ext class loader to find in cache to find the cache. If it is returned, go to the top-level Bootstrap to find it. If not, he will try to load findclass by himself. this class is not found to be responsible for their own load (said above Bootstrap which is responsible for loading), then down to let the child go Ext load, it finds that it is not loaded, layer by layer down to the last custom are If not, the familiar ClassNotFindException will appear. This is the approximate loading process. You can look at the source code ClassLoader.loadClass():

 protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
    
    	
    
        synchronized (getClassLoadingLock(name)) {
    
    
            // First, check if the class has already been loaded
            //1.检查类是否已经被加载了 就是前面的缓存啦
            Class<?> c = findLoadedClass(name);
            if (c == null) {
    
    
                long t0 = System.nanoTime();
                try {
    
    
                    if (parent != null) {
    
    
                    	//2.没有就通知父类加载器去加载啦
                    	// 这里就是递归调用loadClass直到最
                    	//顶级的时候parent不就等于null了吗
                        c = parent.loadClass(name, false);
                    } else {
    
    
                    	//parent==null就BootstrapClass去加载
                        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();
                    //3.如果都是空的话呢 就自己去找咯 
                    //直到自己定义的类加载还没有找到的话就会报
                    //ClassNotFindException
                    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;
        }
        //子类可以实现,模板方法模式
         protected Class<?> findClass(String name) throws ClassNotFoundException {
    
    
        throw new ClassNotFoundException(name);
    }
  }  

The code explains the parental delegation process reasonably. Parental delegation actually means that the child delegates to the father, and the father delegates to the child!
The loadClass method is very important. You must implement this method if you want to customize it.

Custom class loader

Customization only needs to implement loadClass()! Go directly to the code

class MyClassLoader extends ClassLoader{
    
    
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
    
    

        File f = new File("D:/java/code/jvm/", name.replace(".", "/").concat(".class"));
        try {
    
    
            FileInputStream fileInputStream = new FileInputStream(f);
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            int b=0;
            while ((b=fileInputStream.read())!=0){
    
    
                byteArrayOutputStream.write(b);
            }
            byte[] bytes = byteArrayOutputStream.toByteArray();
            byteArrayOutputStream.close();
            fileInputStream.close();//可以写的更加严谨
            return defineClass(name, bytes, 0, bytes.length);
        } catch (FileNotFoundException e) {
    
    
            e.printStackTrace();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
        return super.findClass(name);
    }
}

class HelloClassLoader{
    
    
    static {
    
    
        System.out.println("static");
    }
    public void Hello(){
    
    
        System.out.println("Hello");
    }
    public void m(){
    
    
        System.out.println("Hello Word!");
    }
}
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
    
    

        ClassLoader myClassLoader = new MyClassLoader();
        Class<?> clazz = myClassLoader.loadClass("com.yedi.Hello");
        Hello hello = (Hello) clazz.newInstance();

        System.out.println(hello);

        hello.m();

        Hello hello1 = new Hello();

        System.out.println(hello1);

    }
    输出结果:
    static
	Hello
	com.yedi.Hello@4554617c
	Hello Word
	Hello
	com.yedi.Hello@74a14482

In fact, it is done by reading the file to the byte array, and then converting the byte array into a Class file, because we know that as long as you can convert the file into a Class, you can run on the JVM. This job is the defineClass above. () The method is done.

It's almost over here. Of course, there are more esoteric issues, but there is limited ability.

Guess you like

Origin blog.csdn.net/qq_45422703/article/details/109091005