java虛擬機把描述類的數據從Class文件加載到內存,並對數據進行校驗、轉換解析和初始化,最終造成能夠被虛擬機直接使用的Java類型,這就是虛擬機的類加載機制。java
類從被加載到虛擬機內存中開始,到卸載出內存爲止,它的整個生命週期包括:加載(Loading)、驗證(Verification)、準備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸載(Unloading)7個階段。其中驗證、準備、解析3個部分統稱爲鏈接(Linking),這7個階段的發生順序如圖所示ide
對於任意一個類,都須要由加載它的類加載器和這個類自己一同確立其在Java虛擬機中的惟一性,每個類加載器,都擁有一個獨立的類名稱空間。這句話能夠表達得更通俗一些:**比較兩個類是否「相等」,只有在這兩個類是由同一個類加載器加載的前提下才有意義,不然,即便這兩個類來源於同一個Class文件,被同一個虛擬機加載,只要加載它們的類加載器不一樣,那這兩個類就一定不相等。 **函數
絕大部分Java程序都會使用到如下3種系統提供的類加載器:優化
這個類將器負責將存放在<JAVA_HOME>\lib目錄中的,或者被-Xbootclasspath參數所指定的路徑中的,而且是虛擬機識別的(僅按照文件名識別,如rt.jar,名字不符合的類庫即便放在lib目錄中也不會被加載)類庫加載到虛擬機內存中。啓動類加載器沒法被Java程序直接引用,用戶在編寫自定義類加載器時,若是須要把加載請求委派給引導類加載器,那直接使用null代替便可。this
這個加載器由sun.misc.Launcher $ExtClassLoader實現,它負責加載<JAVA_HOME>\lib\ext目錄中的,或者被java.ext.dirs系統變量所指定的路徑中的全部類庫,開發者能夠直接使用擴展類加載器。spa
這個類加載器由sun.misc.Launcher $App-ClassLoader實現。因爲這個類加載器是ClassLoader中的getSystemClassLoader()方法的返回值,因此通常也稱它爲系統類加載器。它負責加載用戶類路徑(ClassPath)上所指定的類庫,開發者能夠直接使用這個類加載器,若是應用程序中沒有自定義過本身的類加載器,通常狀況下這個就是程序中默認的類加載器。code
java應用程序通常都是由這3種類加載器互相配合進行加載的,若是有必要,還能夠加入本身定義的類加載器。這些類加載器之間的關係通常如圖所示。cdn
上圖中展現的類加載器之間的這種層次關係,稱爲類加載器的雙親委派模型(Parents Delegation Model)。雙親委派模型要求除了頂層的啓動類加載器外,其他的類加載器都應當有本身的父類加載器。這裏類加載器之間的父子關係通常不會以繼承(Inheritance)的關係來實現,而是都使用組合(Composition)關係來複用父加載器的代碼。對象
雙親委派模型的工做過程是:若是一個類加載器收到了類加載的請求,它首先不會本身去嘗試加載這個類,而是把這個請求委派給父類加載器去完成,每個層次的類加載器都是如此,所以全部的加載請求最終都應該傳送到頂層的啓動類加載器中,只有當父加載器反饋本身沒法完成這個加載請求(它的搜索範圍中沒有找到所需的類)時,子加載器纔會嘗試本身去加載。blog
本質上,Android和傳統的JVM是同樣的,也須要經過ClassLoader 將目標類加載到內存,類加載器之間也符合雙親委派模型,類也有對應的生命週期。但基於移動設備的特色,如內存以及電量等諸多方面跟通常的 PC 設備都有本質的區別,Google開發了更符合移動設備的用於執行 Java 代碼的虛擬機,也就是Dalvik和 ART,Android從5.0開始就採用AR虛擬機替代Dalvik。傳統Jvm主要是經過讀取class字節碼來加載, 而ART則是從dex字節碼來讀取. 這是一種更爲優化的方案, 能夠將多個.class文件合併成一個classes.dex文件。
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);
}
}
複製代碼
能夠看到在構造函數裏初始化了DexPathList對象,而在BaseDexClassLoader中的操做findClass
、findResource
執行的都是這個DexPathList對象的操做,關於DexPathList,在此暫不展開。
從DexPathList的構造過程能夠看到,不管optimizedDirectory是何值,傳遞的都是空,因此optimizedDirectory參數是無效的(從Android8.0開始)
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.
public class DexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) {
super(dexPath, null, librarySearchPath, parent);
}
}
複製代碼
DexClassLoader也比較簡單, 只是簡單封裝,和PathClassLoader惟一區別就是多了optimizedDirectory參數,但從上面BaseDexClassLoader
分析能夠知道,從8.0開始optimizedDirectory已經棄用。從理論上來講,PathClassLoader應該能夠徹底替代DexClassLoader。但網上有這樣的結論:
DexClassLoader:可以加載未安裝的apk
PathClassLoader:只能加載系統中已經安裝過的apk
那麼在8.0以上這個結論還成立嗎,事實上PathClassLoader也能夠加載未安裝的apk,驗證過程比較簡單,不在此累贅,有興趣能夠本身試試。