There are two types of classloads in Java, one is user-defined, and the other is the bootstrap class loader built in jvm. All user-defined class loaders are subclasses of java.lang.ClassLoader.
There are three types of class loaders built in jvm, namely Bootstrap ClassLoader, Extension ClassLoader (i.e. ExtClassLoader), and System ClassLoader (i.e. AppClassLoader).
I won’t talk about the parent delegation when jvm is loaded, there are many articles on javaeye that are introduced...
You can take a look at their constructors separately, where the Bootstrap ClassLoader is written in c.
java.lang.ClassLoader
protected ClassLoader(ClassLoader parent) { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkCre ateClassLoader(); }// Assign a value to the parent loader. this.parent = parent; initialized = true; } protected ClassLoader() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkCreateClassLoader(); }//This will Pay AppClassLoader to the parent loader this.parent = getSystemClassLoader(); initialized = true; }
This constructor has two parameters and no constructor. The constructor with parameters passes in the parent loader of this class loader, while the constructor without parameters will treat the class loader returned by getSystemClassLoader() as its own parent loader. Let's look at getSystemClassLoader(). Code
public static ClassLoader getSystemClassLoader() {//The returned class loader is assigned initSystemClassLoader(); if (scl == null) { return null; } Se curityManager sm = System.getSecurityManager(); if (sm != null) { ClassLoader ccl = getCallerClassLoader(); if (ccl != null && ccl != scl && !scl.isAncestor(ccl)) { sm.checkPermission(SecurityConstants. GET_CLASSLOADER_PERMISSION); } } return scl; } private static synchronized void initSystemClassLoader() { if (!sclSet) { if (scl != null) throw new IllegalStateException("recursive invocation"); sun.misc.Launcher l = sun.mis c.Launcher.getLauncher(); if (l != null ) { Throwable oops = null;//The value is assigned scl = l.getClassLoader(); .................................... ..................................... } } sclSet = true; } } The parent class loader here is scl, which is obtained by l.getClassLoader(), getClassLoader(), and then look at the source code of Launcher:
private static Launcher launcher = new Launcher();
public static Launcher getLauncher() { return launcher; } private ClassLoader loader; public Launcher() { // Create the extension class loader ClassLoad er extcl; try {//The parent passed to the constructor here is empty extcl = ExtClassLoader.getExtClassLoader() ; } catch (IOException e) { throw new InternalError( "Could not create extension class loader"); } // Now create the class loader to use to launch the applicat ion try {// You can see that the default loader is AppClassLoader , that is to say, the getSystemClassLoader returns AppClassLoader loader = AppClassLoader.getAppClassLoader(extcl); } catch (IOException e) { throw new Internal Error( "Could not create application class loader"); }//A classload for each current thread, to Prevent confusion caused by classloads in multithreads (I understand this myself, haha) // Also set the context class loader for the primary thread. Thread.currentThread().setContextClassLoader(lo ader); ..... .................................................................................................... ................ } /* * Returns the class loader used to launch the main application. */ public ClassLoader getClassLoader() { return loader; } From this we see that the parent loader of AppClassLoader is ExtClassLoader, and what is the parent loader of ExtClassLoader? Let's look at the ExtClassLoader constructor:
public ExtClassLoader(File[] dirs) throws IOException { super(getExtURLs(dirs), null, factory); this.dirs = dirs; }His parent loader is empty, while his top-level parent class is java.lang.ClassLoader. When the parent passed in is null, when we use ExtClassLoader to load a class, the system will call Bootstrap ClassLoader.
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // First, check if the class has already been loaded Class c = findLoadedClass(name); if (c == null) { try { if (parent != null) {//Call the parent loader first to load. c = parent.loadClass(name, false); } else {//Call the Bootstrap ClassLoader to load c = findBootstrapClass0(name); } } catch (ClassNotFoundException e) { // If still not found, then invoke findClass in order // to find the class. c = findClass(name); } } if (resolve) { resolveClass(c); } return c; }Here, findBootstrapClass0 is to call the Bootstrap ClassLoader, the most core class loader, to load class.
Finally, we can see that the class loader returned by getSystemClassLoader() is AppClassLoader.
Java Classloader mechanism analysis
JDK default ClassLoader
JDK provides the following ClassLoaders by default
Bootstrp loader
The Bootstrp loader is written in C++ language. It is initialized after the Java virtual machine is started. It is mainly responsible for loading the path specified by the %JAVA_HOME%/jre/lib,-Xbootclasspath parameter and the class in %JAVA_HOME%/jre/classes .
ExtClassLoader
The Bootstrp loader loads the ExtClassLoader and sets the parent loader of the ExtClassLoader to the Bootstrp loader. The ExtClassLoader is written in Java, specifically, sun.misc.Launcher$ExtClassLoa der, ExtClassLoader mainly loads %JAVA_HOME%/jre/lib/ext, this All classes directories under the path and class libraries in the path specified by the java.ext.dirs system variable.
AppClassLoader
After the Bootstrp loader loads the ExtClassLoader, the AppClassLoader will be loaded, and the parent loader of the AppClassLoader is specified as the ExtClassLoader. AppClassLoader is also written in Java. Its implementation class is sun.misc.Launcher$AppClassLoader. In addition, we know that there is a getSystemClassLoader method in ClassLoader. This method returns exactly the AppclassLoader.App ClassLoader is mainly responsible for loading the class or It is a jar document, which is also the default class loader for Java programs.
To sum up, the relationship between them can be described in the following figure:
Parent delegation model
ClassLoader loading in Java adopts a parent delegate mechanism. When loading classes using a parent delegate mechanism, the following steps are adopted:
Currently, ClassLoader first checks whether this class has been loaded from the class it has already loaded. If it has been loaded, it will directly return the original class.
Each class loader has its own loading cache. When a class is loaded, it will be put into the cache and can be returned directly when it is loaded next time.
When the classLoader cache is not found, the parent class loader is delegated to load it. The parent class loader adopts the same strategy. First, check its own cache, and then delegate the parent class of the parent class to load it, all the way to bootstrp ClassLoader.
When all the parent class loaders are not loaded, they are loaded by the current class loader and put them in its own cache so that they can be returned directly the next time there is a loading request.
Speaking of this, you may wonder, why does Java adopt such a delegation mechanism? To understand this problem, we introduce another concept "namespace" about Classloader, which means that to determine a certain class, you need a fully qualified name of the class and load this class ClassLoader to jointly determine it. That is to say, even if the fully qualified names of the two classes are the same, because different ClassLoaders load this class, then it is a different class in the JVM. After understanding the namespace, let’s take a look at the delegate model. After adopting the delegate model, the interactive capabilities of different ClassLoaders are increased. For example, as mentioned above, the class libraries provided by our JDK Binsheng, such as hashmap, linkedlist, etc. These classes are loaded by the bootstrp class loader, no matter your program There are so many class loaders in it, so these classes can actually be shared, which avoids confusion caused by different class loaders after loading different classes of the same name.
How to customize ClassLoader
In addition to the default classloader mentioned above, Java also allows applications to customize classloader. If you want to customize classloader, we need to implement it by inheriting java.lang.ClassLoader. Next, let's take a look at customizing it. When classloader, we need to pay attention to several important methods:
1.loadClass method
loadClass method declare
public Class<?> loadClass(String name) throws ClassNotFoundException
The above is the prototype declaration of the loadClass method. The implementation of the parent delegation mechanism mentioned above is actually implemented in this method. Let's take a look at the code of this method to see how it implements parent delegation.
loadClass method implement
public Class<?> loadClass(String name) throws ClassNotFoundException { return loadClass(name, false);}From the above, we can see that the loadClass method calls the loadcClass(name, false) method, so let's take a look at the implementation of another loadClass method.
Class loadClass(String name, boolean resolve)
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // First, check if the class has already been loaded Class c = findLoadedClass(name);//Check whether the class has been loaded if (c == null) { try { if (parent != null) { c = parent.loadClass(name, false); //If it is not loaded and the parent class loader is specified, the parent loader is delegated to load. } else { c = findBootstrapClass0(name);//If there is no parent class loader, delegate the bootstrap loader to load} } catch (ClassNotFoundException e) { // If still not found, then invok e findClass in order // to find the class. c = findClass(name);//If the parent class is not loaded, it will be loaded through its own findClass. } } if (resolve) { resolveClass(c); } return c;}In the above code, I added comments to clearly see how the parent delegation mechanism of loadClass works. One thing we need to note here is that public Class<?> loadClass(String name) throws ClassNotFoundException is not marked as final, which means that we can override this method, which means that the parent delegation mechanism can be broken. In addition, we noticed that there is a findClass method above. Next, let’s talk about whether this method is a bad one.
2.findClass
We check the source code of java.lang.ClassLoader and we find that the implementation of findClass is as follows:
protected Class<?> findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name);}We can see that the default implementation of this method is to directly throw exceptions, but in fact, this method is left to our application to override. The specific implementation depends on your implementation logic. You can read from disk or get the byte stream of class files from the network. After obtaining the class binary, you can hand it over to defineClass for further loading. Let's describe defineClass later. OK, through the above analysis, we can draw the following conclusions:
When we write our own ClassLoader, if we want to follow the parent delegation mechanism, we only need to override findClass.
3. defineClass
Let’s first look at the source code of defineClass:
defineClass
protected final Class<?> defineClass(String name, byte[] b, int off, int len) throws ClassFormatError{ return defineClass(name, b, off, len, null);}From the above code, we can see that this method is defined as final, which means that this method cannot be override. In fact, this is also the only entry left to us by jvm. Through this unique entry, jvm ensures that the class file must comply with The definition of classes specified by the Java virtual machine specification. This method will finally call the native method to implement the loading of the real class.
Ok, through the above description, let’s think about the following question:
If we wrote a java.lang.String class ourselves, can we replace the class that calls JDK itself?
The answer is no. We cannot achieve it. Why? I see many online explanations that the parent delegation mechanism solves this problem, but it is actually not very accurate. Because the parent delegation mechanism can be broken, you can write a classLoader to load the java.lang.String class you wrote, but you will find that it will not load successfully, specifically because it is for the class starting with java.*, jvm The implementation has ensured that it must be loaded by bootstrp.
Scenarios that do not follow the "parent delegation mechanism"
The above mentioned that the parent delegation mechanism is mainly to realize the interaction problem of classes loaded between different ClassLoaders. The classes that are shared by everyone are handed over to the parent loader to load, but there is indeed a need for classes loaded by the parent class loader in Java. The case where the class loaded by the subloader is used. Let’s talk about the occurrence of this situation.
There is a SPI (Service Provider Interface) standard in Java, which uses SPI libraries, such as JDBC, JNDI, etc. We all know that JDBC needs drivers provided by third parties, and the driver jar package is placed in our application itself. classpath, and jdbc's API is part of jdk's provision, and it has been loaded by bootstrp. How to load the implementation class provided by third-party vendors? JAVA introduces the concept of thread context class loading. The thread class loader will inherit from the parent thread by default. If it is not specified, the default is the system class loader (AppClassLoader). In this way, when loading a third-party driver, it is OK Loading through the thread's context class loader.
In addition, in order to implement more flexible class loader OSGI and some Java app servers, it also breaks the parent delegation mechanism.