[JVM] and parent class loader delegate mechanism to resolve instances

ClassLoader plus Living Example

First, the class loader and parents delegate mechanism introduced

In the JVM, the class is loaded into the virtual machine process comprises three steps, i.e. loading, connected and initialized. The loading of this process, by the class loader is ClassLoaderloaded, the class loader is inherently responsible for this duty.

Java itself provides us with several types of class loader, boot class loader Bootstrap ClassLoader, the extension class loader Extension ClassLoader, the application class loader App ClassLoader, in addition to three kinds of the above, we can define our own class loader, then we just need to inherited ClassLoaderclass, override its findClass()methods can be.

Of course, sometimes, you think not you think that when we use a custom class loader to load the class path ClassPatha at classa file, and then call the Classobject getClassLoader()when the method, we find that the class loader loads the class is not our custom loader, as to why? It will be mentioned below. The figure is the JVM class loader mechanism

  • 1, the boot class loaderBootstrap ClassLoader

    Start class loader is responsible for %JRE_HOME/lib/the relevant class files in the directory, for example rt.jar, tools.jarand so on, for example, our Stringclass is stored in rt.jar, the the loader isBootstrap ClassLoader

  • 2, extension classloaderExtension ClassLoader

    Extension class loader is responsible for %JRE_HOME/lib/extthe relevant class files in the directory, for example rt.jar, tools.jaretc.

  • 3, the application class loaderApp ClassLoader

    Application class loader responsible for loading in the application classpath classfiles.

When it comes to issues of class path, we explain that the class path java refers to the value of the system environment variables we configured: CLASSPATH, but not limited to this value. Our local print small series at first classpathvalues:

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

You will find that this is simply not the tools we use it in the catalog, then JVMit is how we loaded into eclipsethe classes written it? The answer is that eclipsehas helped us somehow everything.

Below we give an example, look at Stringwhat kind of class loader is:

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

The above sentence code output null. why? Because if a class is to be Bootstrap ClassLoader, or Extension ClassLoaderloading, getClassLoader()a predetermined output null. And because Stringclass exists rt.jar, it will be Bootstrap ClassLoaderloaded, so output null.

Turning to the class loader, we have to say that class parents delegate mechanism, stating parents delegate mechanism, Xiao Bian summarized in one sentence:

If the custom loader loader has a parent P P1, then it will be loaded before loading the delegate tasks to his father P1, if there is a parent loader P1 P2, it will load the task assigned to P2, if the topmost further Bootstrap ClassLoader load less, then again against the order loaded until a class is loaded ~~

Second, the ClassLoaderclass features introduced

  • 1. Each Classinstance contains a ClassLoaderreference

    Therefore, we can always Classobject getClassLoader()method to get its class loader. Of course, as above said, if a class is to be Bootstrap ClassLoader, or Extension ClassLoaderloading, getClassLoader()a predetermined output null. This is a point we need to pay attention.

  • 2, for an array type []objects, they are not by the class loader ClassLoaderto be loaded, but the Java virtual machine created automatically as needed. Through an array type of Classobject getClassLoader()as the return value of the method with an array of elements used inside the class loader, but if the array element type is the original intHan, the getClassLoader()method returnsnull

    // 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());
    复制代码
  • 3, we can inherit ClassLoaderclasses to implement your own class loader

/**
 * 自定义类加载器
 * @Author jiawei huang
 * @Since 2019年8月7日
 * @Version 1.0
 */
public class MyClassLoader extends ClassLoader {
    // 如果我们从其他地方进行加载,我们可以指定路径
    private String classpath;
    public MyClassLoader(String classPath) {
    	classpath = classPath;
    }
    public MyClassLoader() {
    }
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
    	Class clazz = null;
    	// 获取该class文件字节码数组
    	byte[] classData = getClassData();
    	if (classData != null) {
    		// 将class的字节码数组转换成Class类的实例
    		clazz = defineClass(name, classData, 0, classData.length);
    	}
    	return clazz;
    }
    
    private byte[] getClassData() {
    	byte[] bytes = null;
    	File file = new File(classpath);
    	if (file.exists()) {
    		// 从文件中读取class字节数据
    		FileInputStream in = 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();
    	}
    	return bytes;
    }
}
复制代码

Suppose we exhave a package under the path Main.java, we take a look at the following output:

A.java
MyClassLoader loader = new MyClassLoader();
Class<?> clazzClass = loader.loadClass("ex.Main");
// 1
System.out.println(clazzClass.getClassLoader());
复制代码

Above 1at the output sun.misc.Launcher$AppClassLoader@73d16e93, we might ask, obviously I was using my own class loader to load ex.MainYeah, why was the output of AppClassLoaderthe class loader it? In fact, this is the parents delegate mechanism, MyClassLoaderthe task is loaded to its parent loader App ClassLoader, just ex.Mainturn in the class path, so App ClassLoaderafter loading directly returned.

  • 4, a class throughout the life cycle will only be loaded once, and only once

  • 5, support for parallel load loader called parallel loaders, but only if we have the custom loader initialization, call the ClassLoader.registerAsParallelCapable();method to register themselves, how to do it? We take a look at URLClassLoaderthe source code.

static {
    // 注册自己
    ClassLoader.registerAsParallelCapable();
}
复制代码
  • 6, in the case of commission model is not strictly hierarchical, custom class loader needs to have the ability to parallel, or class loading can lead to deadlock because the loader lock has been held in the class loading process.

We look at the details ClassLoaderof the loadClass()method Source:

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    // 这里有个锁
    synchronized (getClassLoadingLock(name)) {
        1、先检查这个名称的类是否已经被加载过,如果是,就不再加载了,这也印证了我们第4点说的一个类只会被加载一次
        Class<?> c = findLoadedClass(name);
        // 2、如果该类没有被加载,那么就进行加载
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                // 3、如果存在父加载器,就进行委派,这就是双亲委派机制的原理
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    // 4、去`Bootstrap classloader`加载
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }
            // 5、如果3,4都没有加载到,那就执行我们自定义的classloader,这也是为什么我们要重写findClass方法的原因所在
            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 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;
    }
}
复制代码
  • 7, we can not only to classes loaded from the file system, we can also be from the network classload files, access byte[], and then by ClassLoaderthe defineClass()will byte[]be converted into Classan object, and finally through Class.newInstance()to turn it into Java objects ~ ~ ~
ClassLoader loader = new NetworkClassLoader(host, port);
Object main = loader.loadClass("Main", true).newInstance();
复制代码

Third, SpringBootin the constructor why add ClassLoaderparameters?

Because the initialization process, the load()method needs to load class files from different places.

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();
}
复制代码

Guess you like

Origin juejin.im/post/5d4a89a36fb9a06ae61aa1c8