傳統(tǒng)Jvm
java虛擬機把描述類的數(shù)據(jù)從Class文件加載到內(nèi)存朱浴,并對數(shù)據(jù)進行校驗呈昔、轉換解析和初始化,最終形成可以被虛擬機直接使用的Java類型蝶念,這就是虛擬機的類加載機制抛腕。
類的生命周期
類從被加載到虛擬機內(nèi)存中開始芋绸,到卸載出內(nèi)存為止,它的整個生命周期包括:加載(Loading)担敌、驗證(Verification)摔敛、準備(Preparation)、解析(Resolution)全封、初始化(Initialization)马昙、使用(Using)和卸載(Unloading)7個階段。其中驗證刹悴、準備行楞、解析3個部分統(tǒng)稱為連接(Linking),這7個階段的發(fā)生順序如圖所示
類加載器
對于任意一個類土匀,都需要由加載它的類加載器和這個類本身一同確立其在Java虛擬機中的唯一性子房,每一個類加載器,都擁有一個獨立的類名稱空間就轧。這句話可以表達得更通俗一些:**比較兩個類是否“相等”池颈,只有在這兩個類是由同一個類加載器加載的前提下才有意義,否則钓丰,即使這兩個類來源于同一個Class文件躯砰,被同一個虛擬機加載,只要加載它們的類加載器不同携丁,那這兩個類就必定不相等琢歇。 **
雙親委派模型
絕大部分Java程序都會使用到以下3種系統(tǒng)提供的類加載器:
- 啟動類加載器(Bootstrap ClassLoader)
這個類將器負責將存放在<JAVA_HOME>\lib目錄中的,或者被-Xbootclasspath參數(shù)所指定的路徑中的梦鉴,并且是虛擬機識別的(僅按照文件名識別李茫,如rt.jar,名字不符合的類庫即使放在lib目錄中也不會被加載)類庫加載到虛擬機內(nèi)存中肥橙。啟動類加載器無法被Java程序直接引用魄宏,用戶在編寫自定義類加載器時,如果需要把加載請求委派給引導類加載器存筏,那直接使用null代替即可宠互。
- 擴展類加載器(Extension ClassLoader)
這個加載器由sun.misc.Launcher $ExtClassLoader實現(xiàn),它負責加載<JAVA_HOME>\lib\ext目錄中的椭坚,或者被java.ext.dirs系統(tǒng)變量所指定的路徑中的所有類庫予跌,開發(fā)者可以直接使用擴展類加載器。
- 應用程序類加載器(Application ClassLoader)
這個類加載器由sun.misc.Launcher $App-ClassLoader實現(xiàn)善茎。由于這個類加載器是ClassLoader中的getSystemClassLoader()方法的返回值券册,所以一般也稱它為系統(tǒng)類加載器。它負責加載用戶類路徑(ClassPath)上所指定的類庫,開發(fā)者可以直接使用這個類加載器烁焙,如果應用程序中沒有自定義過自己的類加載器航邢,一般情況下這個就是程序中默認的類加載器。
java應用程序一般都是由這3種類加載器互相配合進行加載的骄蝇,如果有必要膳殷,還可以加入自己定義的類加載器。這些類加載器之間的關系一般如圖所示乞榨。
上圖中展示的類加載器之間的這種層次關系秽之,稱為類加載器的雙親委派模型(Parents Delegation Model)当娱。雙親委派模型要求除了頂層的啟動類加載器外吃既,其余的類加載器都應當有自己的父類加載器。這里類加載器之間的父子關系一般不會以繼承(Inheritance)的關系來實現(xiàn)跨细,而是都使用組合(Composition)關系來復用父加載器的代碼鹦倚。
雙親委派模型的工作過程是:如果一個類加載器收到了類加載的請求,它首先不會自己去嘗試加載這個類冀惭,而是把這個請求委派給父類加載器去完成震叙,每一個層次的類加載器都是如此,因此所有的加載請求最終都應該傳送到頂層的啟動類加載器中散休,只有當父加載器反饋自己無法完成這個加載請求(它的搜索范圍中沒有找到所需的類)時媒楼,子加載器才會嘗試自己去加載。
Android的ClassLoader機制
本質上戚丸,Android和傳統(tǒng)的JVM是一樣的划址,也需要通過ClassLoader 將目標類加載到內(nèi)存,類加載器之間也符合雙親委派模型限府,類也有對應的生命周期夺颤。但基于移動設備的特點,如內(nèi)存以及電量等諸多方面跟一般的 PC 設備都有本質的區(qū)別胁勺,Google開發(fā)了更符合移動設備的用于執(zhí)行 Java 代碼的虛擬機世澜,也就是Dalvik和 ART,Android從5.0開始就采用AR虛擬機替代Dalvik署穗。傳統(tǒng)Jvm主要是通過讀取class字節(jié)碼來加載, 而ART則是從dex字節(jié)碼來讀取. 這是一種更為優(yōu)化的方案, 可以將多個.class文件合并成一個classes.dex文件寥裂。
Classloader關系圖
BaseDexClassLoader
public class BaseDexClassLoader extends ClassLoader {
private final DexPathList pathList;
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
super(parent);
this.pathList = new DexPathList(this, dexPath, librarySearchPath, null);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
...
...
Class c = pathList.findClass(name, suppressedExceptions);
...
...
return c;
}
@Override
protected URL findResource(String name) {
return pathList.findResource(name);
}
@Override
public String findLibrary(String name) {
return pathList.findLibrary(name);
}
}
可以看到在構造函數(shù)里初始化了DexPathList對象瘟斜,而在BaseDexClassLoader中的操作findClass
锐涯、findResource
執(zhí)行的都是這個DexPathList對象的操作鼠冕,關于DexPathList飘哨,在此暫不展開梅割。
從DexPathList的構造過程可以看到乃坤,無論optimizedDirectory是何值陶贼,傳遞的都是空悦荒,所以optimizedDirectory參數(shù)是無效的(從Android8.0開始)
PathClassLoader
public class PathClassLoader extends BaseDexClassLoader {
public PathClassLoader(String dexPath, ClassLoader parent) {
super(dexPath, null, null, parent);
}
public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
super(dexPath, null, librarySearchPath, parent);
}
}
PathClassLoader比較簡單, 繼承于BaseDexClassLoader. 默認 optimizedDirectory=null.
DexClassLoader
public class DexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
super(dexPath, null, librarySearchPath, parent);
}
}
DexClassLoader也比較簡單, 只是簡單封裝,和PathClassLoader唯一區(qū)別就是多了optimizedDirectory參數(shù),但從上面BaseDexClassLoader
分析可以知道混萝,從8.0開始optimizedDirectory已經(jīng)棄用遗遵。從理論上來說,PathClassLoader應該可以完全替代DexClassLoader逸嘀。但網(wǎng)上有這樣的結論:
DexClassLoader:能夠加載未安裝的apk
PathClassLoader:只能加載系統(tǒng)中已經(jīng)安裝過的apk
那么在8.0以上這個結論還成立嗎车要,事實上PathClassLoader也可以加載未安裝的apk,驗證過程比較簡單崭倘,不在此累贅翼岁,有興趣可以自己試試。