重新复习理解下java的类加载器
之前比较粗浅的写过一点类加载器的相关知识,最近因为在看相关的内容,所以打算来复习也简单分享下
首先是我们常规用户自定义类的类加载器,1
2
3
4public 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
11StringBuilder 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
4URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (URL url : urls) {
System.out.println(url.toExternalForm());
}
主要是核心的这些包1
2
3
4
5
6
7file:/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
5private 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
17private 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
37protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
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) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
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;
}
}
然后调用当前类加载器的findClass进行处理,这里还是异常就是对下层来说的 c == null 会往下再调用 findClass,这样就完成了往上找再往下逐层尝试加载