ClassLoader的源码学习路径:
ClassLoader源码学习-- 学习源码的方法
ClassLoader源码学习 -- JVM启动之 Launcher,ClassLoader构建
ClassLoader源码学习-- ClassLoader的创建 -- Android Pie
ClassLoader源码学习 -- PathClassLoader,DexClassLoader
大家可能都比较清楚ClassLoader是干什么的,但却不知道ClassLoader从何来,这么多classLoader,死记硬背实在记不下各个ClassLoader是干什么的。
那么,为何不直接看看源码?
下面文章排版不是很好,我就直接说说结果吧
ClassLoader各司其职,在JVM中可以简单理解成不同ClassLoader,加载不同路径的jar或class文件
- BootStrap ClassLoader 加载JAVA_HOME下, jre/lib里面比较重要的jar
- ExtClassLoader 加载JAVA_HOME下,jre/lib/ext 一些扩展包
- APPClassLoader 正式代码运行的上下文,主要是加载我们自己写的类
下面我会用源码,去证实上面的推论:
在java环境下,启动jvm,得使用JRE(java runtime environment)中启动程序入口main()函数。启动JVM不是这次的学习的目标,但我们搞懂Launcher这个类,会对类加载提供很大帮助,因为Java环境下,各个ClassLoader都在Launcher启动。
不知道为什么,jdk下src目录没找到Launcher,于是去Android Studio搜到了这个类,估计是在JRE源码那,有空找个课题研究下。
我们看看Launcher的构造函数。果然就是一堆ClassLoader的初始化。
private static Launcher launcher = new Launcher();
private static String bootClassPath = System.getProperty("sun.boot.class.path");
private ClassLoader loader;
public Launcher() {
Launcher.ExtClassLoader var1;
try {
var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader", var10);
}
try {
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}
Thread.currentThread().setContextClassLoader(this.loader);
String var2 = System.getProperty("java.security.manager");
if (var2 != null) {
SecurityManager var3 = null;
if (!"".equals(var2) && !"default".equals(var2)) {
try {
var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
} catch (IllegalAccessException var5) {
} catch (InstantiationException var6) {
} catch (ClassNotFoundException var7) {
} catch (ClassCastException var8) {
}
} else {
var3 = new SecurityManager();
}
if (var3 == null) {
throw new InternalError("Could not create SecurityManager: " + var2);
}
System.setSecurityManager(var3);
}
}
这里归纳出步骤:
1、bootClassPath,似乎就是传说中的BootStrap ClassLoader的路径
2、创建ExtClassLoader
3、创建AppCLassLoader,并作为当前线程上下文的classLoader使用
4、反射创建SecurityManager (Java环境下安全管理器)
在内部类 BootClassPathHolder中,有关键代码:
File[] var1 = Launcher.getClassPath(Launcher.bootClassPath);
我们可以把Launcher.bootClassPath这个路径打印一下 :
System.out.println(System.getProperty("sun.boot.class.path"))
得到了一堆路径
image.png
可以推测出, BootStrap的作用在于加载jre下的lib的jar包
当然System的配置文件,properties怎么加载,还是无法得知,毕竟是个native函数
private static Properties props;
private static native Properties initProperties(Properties props);
但我们找到了也关心的东西:ExtClassLoader, AppClassLoader
ExtClassLoader,也有类似的代码
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;
}
依葫芦画瓢,打印 System.getProperty("java.ext.dirs") 得出:
/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/ext,
ext目录下的文件
AppClassLoader也可以用相同的方法:
static class AppClassLoader extends URLClassLoader {
final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);
public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
final String var1 = System.getProperty("java.class.path");
final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {
public Launcher.AppClassLoader run() {
URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
return new Launcher.AppClassLoader(var1x, var0);
}
});
}
}
打印 System.getProperty("java.class.path") 得出:
image.png??? 这一坨是什么?这一坨,仅仅是用一个main函数打印刚刚路径的那个类在我电脑的位置,给张图大家看路径对号入座吧。
最后我们得出结论了:
Launcher启动时,分别生成了三个ClassLoader, 三个ClassLoader各司其职
- BootStrap ClassLoader : 加载jre/lib下,jdk核心的几个jar包
- ExtClassLoader 加载jre/lib/ext,正如他的名字,加载ext文件夹下面的jdk扩展功能用的jar。
- AppClassLoader,加载的正是代码工程下的类,所有才有Thread.currentThread().setContextClassLoader(this.loader);
当前线程上下文,使用appClassLoader这一说法。
3个ClassLoader其实从名字就大概能窥探到,主要的用处吧。下一节,我们继续学习ClassLoader。
网友评论