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"))
得到了一堆路徑
可以推測出, 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,
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") 得出:
??? 這一坨是什么?這一坨,僅僅是用一個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。