前言
最近看了很多關(guān)于classloader的文章洞渤,想了解一下classload具體的工作原理。因?yàn)榍皫兹崭栌蚜奶炖常f(shuō)讓我研究研究Android的插件技術(shù)住闯。隨后我搜索一些相關(guān)的資料,其中有一篇博文講到其底層的技術(shù)就是ClassLoader拂酣,所以要對(duì)java的ClassLoader有一定的了解秋冰。當(dāng)然,隨著最近幾天的閱讀婶熬,也對(duì)插件技術(shù)有了一定程度的認(rèn)知剑勾。各種框架,各種技術(shù)流派在國(guó)內(nèi)可謂層出不窮赵颅,阿里的ATLAS虽另,攜程的DynamicAPK,奇虎360的DriodPlugin等等饺谬。但似乎很少看到國(guó)外對(duì)此技術(shù)的熱衷捂刺,雖然Google給了一個(gè)MultiDex,但谷歌還是不建議這么做募寨,尤其隨著ReactNative的興起族展,通過(guò)JsPath即可實(shí)現(xiàn)熱修復(fù)(iOS),相必其可能是未來(lái)的發(fā)展方向拔鹰。當(dāng)然仪缸,通過(guò)對(duì)Android插件技術(shù)的學(xué)習(xí),也是一個(gè)對(duì)Android四大組件(Activity列肢,Service腹殿,Broadcast,Content Provider)充分認(rèn)識(shí)的過(guò)程例书,畢竟背后隱藏的邏輯也逃不過(guò)這些基礎(chǔ)的東西锣尉。基礎(chǔ)中的基礎(chǔ)那么可能就是ClassLoader了吧决采。
關(guān)于ClassLoader
ClassLoader從名字就能看出自沧,類加載器。為什么要類加載器树瞭?這使我想起了.NET的程序集的概念拇厢,很像。一個(gè)Assembly.Load
就能將一個(gè)DLL加載進(jìn)來(lái)晒喷。很遺憾孝偎,沒有對(duì).NET程序集更深層次做挖掘,所以希望能對(duì)java的類加載機(jī)制有一定認(rèn)識(shí)凉敲。我們知道引入一個(gè)類衣盾,只需要import java.io.File
寺旺,沒錯(cuò)import
,為什么使用這個(gè)關(guān)鍵字就能加載势决?對(duì)于java的基礎(chǔ)類庫(kù)來(lái)說(shuō)阻塑,虛擬機(jī)幫你做好了。這個(gè)就跟.NET一樣果复,一個(gè)using
就可以加載全局程序集陈莽,而要加載自定義的程序集,就必須在相同目錄虽抄,即私有程序集走搁。JAVA也一樣,有安裝java的時(shí)候自帶的迈窟,也有你自己定義的私植,你可以把你的jar放在lib目錄,也可以放在當(dāng)前目錄菠隆,隨你兵琳。
編譯的過(guò)程
JAVA屬于解釋型的編程語(yǔ)言狂秘,這個(gè)不用多解釋吧骇径,就是不編譯成最終的目標(biāo)平臺(tái)的二進(jìn)制,而是編譯成中間語(yǔ)言者春,.NET叫IL破衔,JAVA叫.class,叫啥無(wú)所謂钱烟,都一樣晰筛。傳統(tǒng)的編譯過(guò)程是編譯成目標(biāo)文件,然后在對(duì)目標(biāo)文件進(jìn)行鏈接拴袭,但是對(duì)于JAVA而言读第,首先編譯成的是字節(jié)碼.class
文件,在JVM加載class的時(shí)候拥刻,才進(jìn)行鏈接怜瞒。整個(gè)java的執(zhí)行過(guò)程,按照J(rèn)ava Language Specification第12章的介紹般哼,分為如下幾個(gè)過(guò)程:
- JVM運(yùn)行吴汪。
- 加載Main函數(shù)(啟動(dòng)類)
- 對(duì)目標(biāo)類進(jìn)行鏈接(驗(yàn)證,準(zhǔn)備蒸眠,保留)
- 初始化漾橙。
- 創(chuàng)建。
- 終結(jié)一個(gè)類
- 卸載楞卡。
- 程序退出霜运。
三個(gè)類
- BootstrapClassLoader: 這個(gè)是native code寫的脾歇,嵌入在jvm里,虛擬機(jī)啟動(dòng)的時(shí)候自動(dòng)啟動(dòng)bootstrapclassloader觉渴,加載lib下的類庫(kù)介劫。
- ExtensionClassLoader: 負(fù)責(zé)加載lib/ext里面的類庫(kù)。
- ApplicationClassLoader: 負(fù)責(zé)加載
CLASSPATH
里面的類庫(kù)案淋。 - 三者的關(guān)系是:看上圖座韵,下面的繼承上面的類。上面的為下面的parent踢京。
- 加載的順序:先是BootstrapClassLoader加載誉碴,如果它沒有找到,則ExtensionClassLoader嘗試加載瓣距,如果它也沒找到黔帕,則ApplicationClassLoader進(jìn)行加載,都沒找到蹈丸,ClassNotFoundException成黄。
- 加載原理:雙親委托法,即child依次委托parent進(jìn)行查找逻杖。(各博文均介紹如此)
- ClassLoader使用loadClass方法加載一個(gè)類:
protected 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;
}
}
查看繼承關(guān)系
public class LucasMainEntry {
public static void main(String[] args) {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
while(loader != null) {
System.out.println(loader);
loader = loader.getParent();
}
}
}
sun.misc.Launcher$AppClassLoader@2a139a55
sun.misc.Launcher$ExtClassLoader@7852e922
null
我們可以看到對(duì)應(yīng)的父子關(guān)系奋岁。
加載過(guò)程中的問(wèn)題
一個(gè)類如果被兩個(gè)不同的loader加載,那么即便他們有相同的命名空間以及名稱荸百,JVM仍然認(rèn)為他們不是同一個(gè)類闻伶。