Class loader and parent delegate mechanism

In the JVM, a class is loaded into the virtual machine in three steps: load, connect, and initialize. The loading process is done by the ClassLoader, which is naturally responsible for this.

Java provides several types of class loaders, including Bootstrap ClassLoader, Extension ClassLoader, and App ClassLoader. We can also define our own ClassLoader by simply inheriting the ClassLoader class and overriding its findClass() method.

Of course, sometimes what you think is not what you think. When we use a custom class loader to load a class file in the ClassPath, and then call the getClassLoader() method on that class object, we find that the class loader that loaded the class is not our custom loader. And why? This will be covered below. The following figure shows the JVM classloader mechanism

  • 1. Start the Bootstrap ClassLoader

    Jar, tools.jar, etc. For example, our String class is stored in rt.jar. The loader is Bootstrap ClassLoader

  • 2, Extension ClassLoader Extension ClassLoader

    The extension class loader is responsible for the related class files in the %JRE_HOME/jre/lib/ext directory, such as sunec.jar, sunmscapi.jar, etc

  • 3. Apply the App ClassLoader

    Application class loader, responsible for loading the class files under the application classpath.

Speaking of CLASSPATH issues, let’s explain that Java’s CLASSPATH refers to, but is not limited to, the value of our configured system environment variable: CLASSPATH. Our classpath is the local classpath.

.; D:\development\jdk\lib\dt.jar; D:\development\jdk\lib\tools.jar;

You will notice that this is not the directory we use in our development tools at all, so how does the JVM load the classes we write on Eclipse? The answer is that Eclipse has taken care of everything for us.

Here’s an example of what a classloader for a String class is:

System.out.println(String.class.getClassLoader());

This code outputs NULL. Why is that? GetClassLoader () specifies null output if a class is loaded by Bootstrap ClassLoader or Extension ClassLoader. The String class is stored in rt.jar and will be loaded by the Bootstrap ClassLoader, so null is printed.

When it comes to class loaders, we have to talk about the parent delegate mechanism of classes.

If the custom loader P has a parent loader P1, the loading task will be delegated to its parent P1 before loading. If P1 also has a parent loader P2, the loading task will be delegated to P2. If the topmost Bootstrap ClassLoader cannot be loaded, the class will be loaded in reverse order until the class is loaded to ~~

Second,ClassLoaderClass Features

  • 1. Each Class instance contains a ClassLoader reference

    Therefore, we can always passClassThe object’sgetClassLoader()Method to get its class loader. Of course, as mentioned above, if a class is beingBootstrap ClassLoaderorExtension ClassLoaderWhen loading,getClassLoader()Provisions of the outputnull. This is a point that we need to pay attention to.

  • 2. Objects of array type [] are not loaded by the ClassLoader, but are automatically created by the Java virtual machine on demand. The getClassLoader() method returns the same value as the Class loader used for the elements in the array, but returns NULL if the elements are primitive int or something

    // sun.misc.Launcher$AppClassLoader@73d16e93
    ClassLoaderTest[] array = new ClassLoaderTest[5];
    System.out.println(array.getClass().getClassLoader());
    // null
    int[] intArray = new int[3];
    System.out.println(intArray.getClass().getClassLoader());
    Copy the code
  • 3. We can implement our own ClassLoader by inheriting the ClassLoader class

Public MyClassLoader extends ClassLoader public MyClassLoader extends ClassLoader {// If we load from somewhere else, we can specify the path private String classpath; public MyClassLoader(String classPath) { classpath = classPath; } publicMyClassLoader() { } @Override protected Class<? > findClass(String name) throws ClassNotFoundException { Class clazz = null; Byte [] classData = getClassData();if(classData ! Clazz = defineClass(name, classData, 0, classdata.length); }return clazz;
    }
    
    private byte[] getClassData() {
    	byte[] bytes = null;
    	File file = new File(classpath);
    	if(file.exists()) {// Reads class byte data FileInputStream from a filein = null;
    		ByteArrayOutputStream out = null;
    		try {
    			in = new FileInputStream(file);
    			out = new ByteArrayOutputStream();
    
    			byte[] buffer = new byte[1024];
    			int size = 0;
    			while((size = in.read(buffer)) ! = -1) { out.write(buffer, 0, size); } } catch (IOException e) { e.printStackTrace(); } finally { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } bytes = out.toByteArray(); }returnbytes; }}Copy the code

Suppose we have main. Java under the ex package path, look at our output:

A.java MyClassLoader loader = new MyClassLoader(); Class<? > clazzClass = loader.loadClass("ex.Main");
// 1
System.out.println(clazzClass.getClassLoader());
Copy the code

Sun.misc.Launcher$AppClassLoader@73d16e93 is printed at the top of the list. We might ask, why am I printing the AppClassLoader when I’m using my own classloader to load ex.Main? MyClassLoader assigns the load to its parent App ClassLoader. Ex.Main is in the classpath, so the App ClassLoader returns the load directly.

  • 4. A class is loaded only once in its entire life cycle, once and only

  • 5, support for parallel loading of the loader is called parallel loader, but the premise is that we have to customize loaders initialization, call this. RegisterAsParallelCapable (); Method to register itself, how do you do that? Let’s look at the source of URLClassLoader.

Static {/ / registered himself this registerAsParallelCapable (); }Copy the code
  • 6. In cases where the delegate model is not strictly hierarchical, custom class loaders need to be parallelistic, otherwise class loading may result in deadlocks because the loader lock is held throughout the class loading process.

LoadClass () : loadClass();

protected Class<? > loadClass(String name, Boolean resolve) throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) { If a Class has been loaded before, it will not be loaded again. If so, it will not be loaded again. > c = findLoadedClass(name); // if the class is not loaded, load itif(c == null) { long t0 = System.nanoTime(); If there is a parent loader, delegate. This is how the parent delegate mechanism worksif(parent ! = null) { c = parent.loadClass(name,false);
                } else{// 4, 'Bootstrap classloader' load c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrownifClass not found // from the non-null parent classloader} That's why we're overriding the findClass method, rightif (c == null) {
                // If still not found, then invoke findClass inorder // to find the class. long t1 = System.nanoTime(); 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);
        }
        returnc; }}Copy the code
  • 7. We can load classes not only from the file system, but also from the networkclassFile load, getbyte[]And then throughClassLoaderthedefineClass()willbyte[]Converted toClassObject, and finally passClass.newInstance()It can be converted into Java objects
ClassLoader loader = new NetworkClassLoader(host, port);
Object main = loader.loadClass("Main".true).newInstance();
Copy the code

Three,SpringBootWhy is the constructor addedClassLoaderParameters?

The load() method needs to load the class file from a different place during initialization.

protected void load(ApplicationContext context, Object[] sources) {
    if (logger.isDebugEnabled()) {
    	logger.debug(
    			"Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
    }
    BeanDefinitionLoader loader = createBeanDefinitionLoader(
    		getBeanDefinitionRegistry(context), sources);
    if(this.beanNameGenerator ! = null) { loader.setBeanNameGenerator(this.beanNameGenerator); }if(this.resourceLoader ! = null) { loader.setResourceLoader(this.resourceLoader); }if(this.environment ! = null) { loader.setEnvironment(this.environment); } loader.load(); }Copy the code