The classloader structure in the JVM
To understand the classloader structure in the JVM, it is not enough to consult the documentation. Here is a small program to help you understand the class loader structure in a JVM virtual machine.
package com.wuyue.demo; import java.util.Date; import java.util.List; Public class JVMClassLoader {public static void main(String[] args){system.out.println ("Name of the loader for the JVMClassLoader class :"+JVMClassLoader.class.getClassLoader().getClass().getName());
System.out.println("System class loader name :"+System.class.getClassLoader());
System.out.println("Name of the loader for the List class :"+List.class.getClassLoader());
ClassLoader cl = JVMClassLoader.class.getClassLoader();
while(cl ! = null){ System.out.print(cl.getClass().getName()+"- >"); cl = cl.getParent(); } System.out.println(cl); }}Copy the code
Then we compile and run the program to see the result of this code:
Why do some classes have null loaders?
Click here to view the JDK documentation
If you look at the function description in the JDK, you’ll find this:
Returns the class loader for the class. Some implementations may use null to represent the bootstrap class loader. This method will return null in such implementations if this class was loaded by the bootstrap class loader.
Some virtual machine implementations use null instead of the bootstrap classloader.
What to make of the three types of loaders printed out?
BootStrap->ExtClassLoader->AppClassLoader-> developer custom class loader. BootStrap can be regarded as the ancestor loader and the developer custom class loader as the underlying loader. But most of the time, we don’t customize class loaders, so for the most part, AppClassLoader is the underlying class loader in the JVM.
Note that BootStrap is written in c++ code, and the last two class loaders are written in Java classes. This explains why the BootStrap loader returns null, because the ancestor class loader is not found in Java
Delegate mechanism principles for class loading
- If it is loaded from bottom to top, the top layer cannot be loaded and then the bottom layer cannot be loaded. If it is not loaded from the bottom layer, the ClassNotFound error is reported.
- JVMClassLoader: AppClassLoader: JVMClassLoader: AppClassLoader: JVMClassLoader: AppClassLoader: JVMClassLoader Finally, find the class in AppClassLoder. So the loader for this class is AppClassLoader.
- Why is the System and List classloaders the Boot classloaders? The default path for the Boot class loader to load is rt.jar in the /jre/lib directory. The default path to the ext loader is /jre/lib/ext/*.jar. The JVMClassLoader class cannot be found in either of these directories. Note that the root directory is the installation directory of your JDK
How to verify the previous conclusion?
Many people learn about class loaders only after browsing through the document, it is difficult to have a deep image, after a long time to forget, so the following example can deepen the impression of class loaders delegate mechanism
/jre/lib/ext/ is the directory where the ext class loader is to be loaded. And then when we run the program that we wrote in the beginning again, we can see that this same class JVMClassLoader, our classloader started out as appClassLoader and then became extClassLoader. This should give you a good idea of the delegate mechanism of the class loader.
How do you evaluate the classloader mechanism under this delegation mechanism?
In a nutshell, the classloader mechanism in the JVM can be summarized in one sentence:
If you can use your father’s money, never use your own money. If your father has no money, then use your own money. If you still have no money, then classNotFound exception
The advantage of loading a class is that it is first handed over to its parent class loader, and if the parent class has it, it is used directly, so that it does not need to load a class again if it has been loaded before. Abbreviation: can gnaw old use father’s money, why use own?
Look at the source code again to deepen the understanding of the class loader.
protected Class<? > loadClass(String name, Boolean resolve) throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) {// Check if this class has been loaded Class c = findLoadedClass(name);if (c == null) {
long t0 = System.nanoTime();
try {
if(parent ! C = parent. Loadclass (name, parent) {// call loadClass recursively.false);
} else{// If there is no parent class loader, that's the end of the story. C = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrownif class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass inorder // to find the class. long t1 = System.nanoTime(); // If not, call findClass to find the class. 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;
}
Copy the code
If the parent cannot be found, call the findClass method to find it. This is consistent with our previous analysis.
If you are interested, you can find rt.jar in the JDK directory. You can check the AppClassLoader and other system classLoader source code, which helps to improve your understanding. Here is not too much description
Custom class loaders.
First we define a CustomDate class that just overrides the toString method
package com.wuyue.test; import java.util.Date; Public class CustomDate extends Date{@override public StringtoString() {
return "my cystom date"; }}Copy the code
Then write a simple classloader, custom type.
package com.wuyue.test;
import java.io.*;
public class MyClassLoader extends ClassLoader{
String classDir;
public MyClassLoader() { } public MyClassLoader(String classDir) { this.classDir = classDir; } @Override protected Class<? > findClass(String name) throws ClassNotFoundException { String classFile=classDir+"/"+name+".class";
System.out.println("classFile path=="+classFile); Try {// we simply read the file stream to get the byte array // instead, we can try to encrypt the class file and decrypt it later so that the class file can only be read by the classloader you wrote. // No other classloader can read the system. byte[] classByte=toByteArray(classFile);return defineClass(classByte,0,classByte.length);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return super.findClass(name);
}
/**
* the traditional io way
*
* @param filename
* @return
* @throws IOException
*/
public static byte[] toByteArray(String filename) throws IOException, FileNotFoundException {
File f = new File(filename);
if(! f.exists()) { throw new FileNotFoundException(filename); } ByteArrayOutputStream bos = new ByteArrayOutputStream((int) f.length()); BufferedInputStreamin = null;
try {
in = new BufferedInputStream(new FileInputStream(f));
int buf_size = 1024;
byte[] buffer = new byte[buf_size];
int len = 0;
while(1! = (len = in.read(buffer, 0, buf_size))) { bos.write(buffer, 0, len); }returnbos.toByteArray(); } catch (IOException e) { e.printStackTrace(); throw e; } finally { try { in.close(); } catch (IOException e) { e.printStackTrace(); } bos.close(); }}}Copy the code
Finally write a test we custom classloader main program:
package com.wuyue.test;
import java.util.Date;
public class ClassLoaderTest {
public static void main(String[] args)
{
try {
Class classDate = new MyClassLoader("/Users/wuyue/IdeaProjects/ClassLoaderTest/out/production/ClassLoaderTest/com/wuyue/test").loadClass("com.wuyue.test.CustomDate");
Class classDate2 = new MyClassLoader("/Users/wuyue/IdeaProjects/ClassLoaderTest/out/production/ClassLoaderTest/com/wuyue/test").loadClass("CustomDate");
Date date = (Date) classDate.newInstance();
System.out.println("date ClassLoader:"+date.getClass().getClassLoader().getClass().getName());
System.out.println(date);
Date date2 = (Date) classDate2.newInstance();
System.out.println("date2 ClassLoader:"+date2.getClass().getClassLoader().getClass().getName()); System.out.println(date2); } catch (Exception e1) { e1.printStackTrace(); }}}Copy the code
Then let’s have a look at the results of the program:
As you can see, the only difference between classDate and classDate2 is that classDate is passed the full package name, while classDate2 is not. As a result, the classLoader of the former is still the appClassLoader of the system, while the latter is our custom classLoader. The reason:
For classDate and classDate2, we manually specify that her classload is our custom MyClassLoader, but according to the classloader rules, we can use the father’s loadClass and will not use our own loadclass. The AppClassLoader must pass in the full package name for the loadClass to succeed. The classDate constructor still passes in the full package name, which is why the classDate loader is still AppClassLoader. ClassDate2 does not pass in the full package name, so AppClassLoader cannot find the CustomDate class. Finally, we can only give MyClassLoader, the lowest level, our custom classloader to load