之前比较粗浅的写过一点类加载器的相关知识,最近因为在看相关的内容,所以打算来复习也简单分享下
首先是我们常规用户自定义类的类加载器,
1 2 3 4
| public class ClassLoaderDemo { public static void main(String[] args) { ClassLoader classLoader = ClassLoaderDemo.class.getClassLoader(); System.out.println(classLoader);
|
输出的是
1
| sun.misc.Launcher$AppClassLoader@18b4aac2
|
是这个AppClassLoader
之前也讲到过,常规的层级就是
用户自定义类加载器 -> AppClassLoader -> ExtClassLoader -> BootstrapClassLoader
可以用一段简单的代码来看下
1 2 3 4 5 6 7 8 9 10 11
| StringBuilder sb = new StringBuilder("|--"); boolean needContinue = true; while (needContinue) { System.out.println(sb.toString() + classLoader); if (classLoader == null) { needContinue = false; } else { classLoader = classLoader.getParent(); sb.insert(0, "\t"); } }
|
可以看到
1 2 3
| |--sun.misc.Launcher$AppClassLoader@18b4aac2 |--sun.misc.Launcher$ExtClassLoader@555590 |--null
|
最后的就是 BootstrapClassLoader,因为是native实现的,所以没有类名
那么到这呢,就是想接着说下双亲委派,这个翻译可能跟实际的理解不一定一样属于见仁见智
个人的观点主要是第一是保证了一致性,不会随意的使用classloader,对于比如jdk提供的类,应该由什么类加载器来加载是稳定可预期的,另一个就是各司其职,
我们可以看下BootstrapClassLoader类加载器主要负责加载哪些类
1 2 3 4
| URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs(); for (URL url : urls) { System.out.println(url.toExternalForm()); }
|
主要是核心的这些包
1 2 3 4 5 6 7
| file:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/resources.jar file:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/rt.jar file:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/jsse.jar file:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/jce.jar file:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/charsets.jar file:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/jfr.jar file:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/classes
|
而ExtClassLoader
则是负责 /jre/lib/ext 下面的类
因为没有公开方法
我们捞一下
其实代码都在 sun.misc.Launcher 里面
创建的时候
1 2 3 4 5
| private static ExtClassLoader createExtClassLoader() throws IOException { try { return (ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<ExtClassLoader>() { public ExtClassLoader run() throws IOException { File[] var1 = Launcher.ExtClassLoader.getExtDirs();
|
而这个 getExtDirs() 就是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| private static File[] getExtDirs() { String var0 = System.getProperty("java.ext.dirs"); File[] var1; if (var0 != null) { StringTokenizer var2 = new StringTokenizer(var0, File.pathSeparator); int var3 = var2.countTokens(); var1 = new File[var3];
for(int var4 = 0; var4 < var3; ++var4) { var1[var4] = new File(var2.nextToken()); } } else { var1 = new File[0]; }
return var1; }
|
打印出来就是
/Users/user/Library/Java/Extensions:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/ext:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java
在这些目录下的
AppClassLoader
则是当前应用程序的classpath下的类
这里就有很多了,不过classpath好像也是个知识点,可以后面讲讲,这块主要还是之前也提过,首先是找这个类的实例化对象,找到了就可以没有就调用父级的loadClass,实现逐层找,找不到在通过本加载器的findClass去加载。这里可以稍微想一下,如果有父类加载器就让parent加载,parent也是再往上,当这个parent是比如Ext时,委托它的parent就是Bootstrap,加载为空时,就会走到第二个if (c==null)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { }
if (c == null) { long t1 = System.nanoTime(); c = findClass(name);
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } }
|
然后调用当前类加载器的findClass进行处理,这里还是异常就是对下层来说的 c == null 会往下再调用 findClass,这样就完成了往上找再往下逐层尝试加载