This article posted the class loader related source code has been simplified, as long as CRUD can understand. Not all JDK source code is open source. If you want to see the full source code for yourself and find that the Sun package source is not available in Eclipse, you can skip to the last section. But I strongly recommend not reading through the source code aimlessly.
○ What does a class loader do?
When you first learned Java, you should have compiled Java files on the command line. Java code is compiled into class files through javac, and the class loader is used to load class files into the virtual machine.
Get the binary stream describing the class by its fully qualified name outside of the virtual machine, and let the application decide how to do it.
At a macro level, there are only two types of loaders: boot class loaders and other class loaders.
Startup class loader belongs to a part of the virtual machine, it is written in C++, can not see the source; Other classloaders are written in Java, which is simply Java classes, as you’ll see in a moment, such as extension classloaders and application classloaders.
- Start the class loader: BootstrapClassLoader
- Extended class loader: ExtentionClassLoader
- Application class loader: AppClassLoader (also called “System class loader”)
Why use multiple loaders when you’re just loading class files into the virtual machine? When the Java virtual machine starts up, it does not load all of the class files at once (memory will burst). Instead, it loads them dynamically as needed.
What are they loaded with?
The classloader finds a class file by its fully qualified name (or absolute path). BootstrapClassLoader: BootstrapClassLoader: BootstrapClassLoader
In this section, you only care about the output, because I’m going to look up these apis anyway.
URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (URL url : urls) {
System.out.println(url);
}Copy the code
Output (%20 is a space) :
File: / C: / Program files/Java / % 20 jre1.8.0 _131 / lib/resources. The jar file: / C: / Program files/Java / % 20 jre1.8.0 _131 / lib/rt. The jar File: / C: / Program files/Java / % 20 jre1.8.0 _131 / lib/sunrsasign jar file: / C: / Program files/Java / % 20 jre1.8.0 _131 / lib/jsse jar File: / C: / Program files/Java / % 20 jre1.8.0 _131 / lib/jce jar file: / C: / Program files/Java / % 20 jre1.8.0 _131 / lib/charsets. The jar File: / C: / Program files/Java / % 20 jre1.8.0 _131 / lib/JFR jar file: / C: / Program files/Java / % 20 jre1.8.0 _131 / classesCopy the code
As you can see, the startup class loader loads the JRE and the core libraries in the JRE /lib directory, depending on where your JRE is installed. Print the loading path of the ExtentionClassLoader again and see:
URL[] urls = ((URLClassLoader) ClassLoader.getSystemClassLoader().getParent()).getURLs();
for (URL url : urls) {
System.out.println(url);
}Copy the code
Output result:
File: / C: / Program files/Java / % 20 jre1.8.0 _131 / lib/ext/access - bridge - 64. The jar File: / C: / Program files/Java / % 20 jre1.8.0 _131 / lib/ext/cldrdata jar File: / C: / Program files/Java / % 20 jre1.8.0 _131 / lib/ext/DNSNS jar File: / C: / Program files/Java / % 20 jre1.8.0 _131 / lib/ext/dns_sd jar File: / C: / Program files/Java / % 20 jre1.8.0 _131 / lib/ext/jaccess jar File: / C: / Program files/Java / % 20 jre1.8.0 _131 / lib/ext/JFXRT jar File: / C: / Program files/Java / % 20 jre1.8.0 _131 / lib/ext/localedata jar File: / C: / Program files/Java / % 20 jre1.8.0 _131 / lib/ext/nashorn jar File: / C: / Program files/Java / % 20 jre1.8.0 _131 / lib/ext/sunec jar File: / C: / Program files/Java / % 20 jre1.8.0 _131 / lib/ext/sunjce_provider jar File: / C: / Program files/Java / % 20 jre1.8.0 _131 / lib/ext/sunmscapi jar File: / C: / Program files/Java / % 20 jre1.8.0 _131 / lib/ext/sunpkcs11 jar File: / C: / Program files/Java / % 20 jre1.8.0 _131 / lib/ext/zipfs jarCopy the code
Obviously, the extension classloader loads the extension package in the JRE /lib/ext directory. It doesn’t matter what these libraries are, just that different libraries may be loaded by different class loaders.
How does the JVM know where we installed the JRE? Because you installed the JDK after configuring the environment variables ah! That’s what JAVA_HOME, CLASSPATH and the like are for.
Finally, AppClassLoader:
URL[] urls = ((URLClassLoader) ClassLoader.getSystemClassLoader()).getURLs();
for (URL url : urls) {
System.out.println(url);
}Copy the code
Output result:
file:/D:/JavaWorkSpace/PicklePee/bin/Copy the code
This is the bin directory of the current Java project, where our own Java code is compiled into the class file.
Java Virtual Machine entry
When we run a Java program, the JVM. DLL in the JDK installation directory starts the virtual machine, and the Sun.misc.Launcher class is the first piece of Java code the virtual machine executes. As mentioned earlier, with the exception of BootstrapClassLoader, all class loaders are implemented in Java — you can see them in the Launcher.
Here is a simplified version of the source code for Sun.misc.Launcher that should be easy to read:
public class Launcher { private static Launcher launcher = new Launcher(); private ClassLoader appClassLoader; Private static String bootClassPath = System.getProperty("sun.boot.class.path"); private static String bootClassPath = System.getProperty("sun.boot.class.path"); public Launcher() { ClassLoader extentionClassLoader; / / extension class loader here try {extentionClassLoader = ExtClassLoader. GetExtClassLoader (); } catch (Exception e) {... } try {// Apply class loaders here, get takes extension class loaders as arguments, we'll come back to this later. appClassLoader = AppClassLoader.getAppClassLoader(extentionClassLoader); } catch (Exception e) {... Static class ExtClassLoader extends URLClassLoader {} static class ExtClassLoader extends URLClassLoader {} Apply the class loader, The parent is URLClassLoader static class AppClassLoader extends URLClassLoader {} public static Launcher getLauncher() {return launcher; } public ClassLoader getClassLoader() { return appClassLoader; } private static URLStreamHandlerFactory factory = new Factory(); private static class Factory implements URLStreamHandlerFactory { public URLStreamHandler createURLStreamHandler(String Protocol) {/* Create a File Handler through which class files on our hard disk enter memory */}}}Copy the code
As you can see, both the extension classloader and the application classloader are static inner classes in the Launcher. They both call their own static method getExtClassLoader to return their own instance. Look what happens:
/* The extension class loader is the static inner class of the Launcher, */ static class ExtClassLoader extends URLClassLoader {public ExtClassLoader(File[] dirs) throws ExtClassLoader(File[] dirs) IOException {// is handled by the parent URLClassLoader. The middle argument is null, what is it? super(getExtURLs(dirs), null, factory); } public static ExtClassLoader getExtClassLoader() throws IOException { final File[] dirs = getExtDirs(); ExtClassLoader extentionClassLoader = new ExtClassLoader(dirs); Return extentionClassLoader} private static File[] getExtDirs() {return extentionClassLoader} private static File[] getExtDirs() {// This is the same path we printed in section 1. String s = System.getProperty("java.ext.dirs"); File[] dirs; . According to the; Split string s into a File array... return dirs; }}Copy the code
We just passed three parameters to the constructor of the parent URLClassLoader, continuing further:
public class URLClassLoader extends SecureClassLoader implements Closeable { private ArrayList<URL> path = new ArrayList<URL>(); public URLClassLoader(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) { super(parent); // for (int I = 0; int I = 0; i < urls.length; i++) { path.add(urls[i]); } if (factory ! = null) { jarHandler = factory.createURLStreamHandler("jar"); }}}Copy the code
URLClassLoader continues to throw this null to the parent SecureClassLoader, okay? Let’s see what it does:
public class SecureClassLoader extends ClassLoader { protected SecureClassLoader(ClassLoader parent) { super(parent); . }... }Copy the code
Nothing is done and is thrown directly to the parent class:
public abstract class ClassLoader { private final ClassLoader parent; private ClassLoader(Void unused, ClassLoader parent) { this.parent = parent; // parent is a global variable in the ClassLoader... } protected ClassLoader(ClassLoader parent) {this(checkCreateClassLoader(), parent); } protected ClassLoader() {// Default parent is system (application) ClassLoader! this(checkCreateClassLoader(), getSystemClassLoader()); } public final ClassLoader getParent() { if (parent == null) { return null; } return parent; } /* Prevent malicious code from affecting the system, */ Private static Void checkCreateClassLoader() {SecurityManager Security = System.getSecurityManager(); if (security ! = null) { security.checkCreateClassLoader(); } return null; }}Copy the code
Null is passed to the parent global variable of the topmost ClassLoader(getExtClassLoader).
You may have noticed that the JDK always uses a method like System.getProperty(” XXX “) to get the class file path. Where does this string argument come from? It can be manually assigned at startup time. Such as:
Java -d java.ext.dirs= path MyClass // The custom path will override Java's own extended classpath. Another command is -xbootclasspath, which can change the load path of the core library.Copy the code
Going back to the source code, remember that line of code in the beginning Launcher class that got the application class loader?
appClassLoader = AppClassLoader.getAppClassLoader(extentionClassLoader); Copy the code
It passes the extension classloader created as a parameter to the application classloader.
static class AppClassLoader extends URLClassLoader { public static ClassLoader getAppClassLoader(final ClassLoader extcl) throws IOException { final String s = System.getProperty("java.class.path"); Final File[] path = getClassPath(s); URL[] urls = pathToURLs(path); Return new AppClassLoader(urls, extcl); } AppClassLoader(URL[] urls, ClassLoader parent) {// AppClassLoader's parent is URLClassLoader, The second argument changes from null to the extension classloader super(urls, parent, factory); }}Copy the code
Everything should be clear at this point, and the rest of the process is the same as extending the classloader! Except that the parent argument is eventually assigned to the extended class loader (extCL) instead of NULL. So what does the parent really do?
Parent loader
The parent in the ClassLoader is the parent loader. You just looked at the class loader inheritance diagram, but a parent loader is not a parent class, which are two different concepts. Looking at the getParent() method of the ClassLoader, any object that a ClassLoader calls on this method is its parent loader.
In addition to the BootstrapClassLoader, each class loader has a parent loader. For example, the application class loader, its parent is the extension class loader. You might say that the parent of the extension class loader is null, so it has no parent loader?
Yes, its parent loader is BootstrapClassLoader. Any loader whose parent is null and whose parent is BootstrapClassLoader, keep this in mind and you’ll see why soon.
One last question, if you implement a ClassLoader directly from the ClassLoader and do not specify a parent, what is the parent of the custom ClassLoader?
Is the application class loader AppClassLoader. You can pull back and look at the ClassLoader’s no-argument constructor.
4. Parental delegation model
There is a term that describes the process by which class loaders load classes: the parent delegate model. This is a misleading term, however; it should be called the parent-Delegation Model. But there was no way. Everyone was already calling it that. The parent delegate refers to the global variable parent in the ClassLoader.
The process of parental delegation is as follows:
- When a classloader receives a classloading task, it checks to see if it is in the cache. If not, it delegates the task to its parent loader.
- The parent loader does the same thing, delegating layer by layer until the topmost class loader starts.
- If the startup class loader does not find the class to load, it returns the loading task to the next class loader, which does the same.
- If the lowest class loader still does not find the required class file, it throws an exception.
So one line goes up and down, and there are no “parents”. The Java implementation of the whole process is no mystery:
Public abstract class ClassLoader {// name: the absolute path of the class file // resolve: resolve immediately after it is found. protected Class<? > loadClass(String name, Boolean resolve) throws ClassNotFoundException {synchronized (lock) { This is why you need to restart the JVM to make Class< changes? > target = findLoadedClass(name); // Native method if (target == null) {try {if (parent! Target = parent. LoadClass (name, false); } else {// If the parent loader is null, delegate to the BootstrapClassloader target = findBootstrapClassOrNull(name); // native method}} catch (ClassNotFoundException e) {... If (target == null) {if (target == null) {target = findClass(name); } } if (resolve) { resolveClass(target); // native method} return target; }} // findClass is a template method that needs to be overridden with protected Class<? > findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); }}Copy the code
What is parsing? Change a symbolic reference to a direct reference. For example, com.test.Car has a com.test.Wheel class.Car does not know the actual memory address of Wheel at compile time, so com.test.Wheel is just a symbol. “Parsing” means loading the referenced class into memory and turning the com.test.wheel symbol into a pointer to an in-memory target.
Now that we have the findClass template method, URLClassLoader inherits the ClassLoader and overrides it to do three things:
protected Class<? > findClass(final String name) throws ClassNotFoundException {// 1, security check // 2, read the class file from disk into memory according to the absolute path byte[] raw = getBytes(name); Return defineClass(raw); return defineClass(raw); }Copy the code
If we were to implement a ClassLoader ourselves, we would basically inherit the ClassLoader and override the findClass method with defineClass at the end of the method.
Five, why parents delegate?
Ensure that the class is globally unique.
If you write a class with the same name as a class in the core library, you will find that the class will compile normally, but will never be loaded and run. Because the class you’re writing isn’t going to be loaded by the application class loader, it’s going to be delegated to the top level, and it’s going to be found by the launcher class loader in the core library. Without parental delegation to ensure that classes are globally unique, anyone could write a java.lang.Object class and put it in the classpath, and the application would be a mess.
From a security point of view, the Java virtual machine always looks for types from the most trusted Java core API through the parent delegation mechanism, which prevents untrusted classes from posing as trusted classes and causing harm to the system.
6. So what’s the use of knowing all this?
- The interview.
- The principle of Servlet containers such as Tomcat and JBoss may require another chapter.
- If you don’t want your code decompiled, you can encrypt the compiled code and decrypt it with your own classloader.
- I can’t make this up anymore.
Use Eclipse to view sun package source code
Sun package source code is not normally visible.
To view this section of the source code, determine your current Java version (CMD type java-version)
Then download the corresponding OpenJDK. From the Internet download speed may be relatively slow, I made a set of 6-12 version on baidu network disk.
Link: https://pan.baidu.com/s/1ZwllHyhxSDihAhi8ZqustA extraction code: cx2cCopy the code
Eclispe Window → Preferences:
After the External Folder is displayed, open the newly decompressed OpenJDK Folder and find the SRC path:
Then restart Eclipse. Done!