什么是類加載機制?
Java虛擬機將編譯后的.class文件加載到內存中,進行校驗、轉換、解析和初始化,到最終的使用,這就是類的加載機制。類的加載時機并未有明確的規定,但是類明確了類的初始化時機。
類的加載機制大致分為五個過程:加載、驗證、準備、解析、初始化。
通過ClassLoader加載一個Class對象到內存中。具體過程:
驗證加載后的類是否符合.Class文件結構,類數據是否符合虛擬機的要求,確保不會危害虛擬機的安全。具體過程如下:
將類符號引用轉換成直接引用。
給類變量(static)賦值,并執行static{}方法。這里的觸發執行的方法是類構造器中。
類初始化的時機:
這里的ClassLoader是安卓的類加載器,不是Java的加載器,這是有區分的,比如Java的類加載器加載的是jar里面的.class文件的集合,而安卓則是將.class文件的集合全部寫入到一個dex文件中,刪除一些重復的代碼,以此來提高性能。
Android的類加載器類型也可以分為兩種:
無論哪種加載器,它們都要繼承ClassLoader這個抽象父類。其中系統類加載器主要有:BootClassLoader、PathClassLoader、DexClassLoader
(1) BootClassLoader
class BootClassLoader extends ClassLoader { private static BootClassLoader instance; @FindBugsSuppressWarnings("DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED") public static synchronized BootClassLoader getInstance() { if (instance == null) { instance = new BootClassLoader(); } return instance; } ...}
BootClassLoader繼承于ClassLoader,它是一個沒有父加載器的加載器,它在Zygote進程啟動的時候,BootClassLoader加載器將會被創建,用它加載一些預加載類,方便以后fork進程時復用資源。同時它也是ClassLoader的內部類。
(2) PathClassLoader
public class PathClassLoader extends BaseDexClassLoader { /** * @param dexPath : Dex相關文件的路徑 * @param parent : 父加載器 */ public PathClassLoader(String dexPath, ClassLoader parent) { super(dexPath, null, null, parent); } /** * @param dexPath: Dex相關文件的路徑 * @param librarySearchPath:包含C/C++庫的路徑集合 * @param parent : 父加載器 */ public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) { super(dexPath, null, librarySearchPath, parent); } ...}
PathClassLoader繼承BseDexClassLoader,同時BaseDexClassLoader繼承ClassLoader。 PathClassLoader的創建在system_server進程中,PathClassLoader類加載器通常加載已經安裝的apk的dex文件。 PathClassLoader類加載器默認的解壓的dex文件的存儲路徑是:/data/dalvik_cache路徑中。 如下是創建的時機:
public class ZygoteInit { // 創建完system_server進程后,會執行此方法 private static Runnable handleSystemServerProcess(ZygoteArguments parsedArgs) { if (systemServerClasspath != null) { //... } else { ClassLoader cl = null; // 創建PathClassLoader加載器 if (systemServerClasspath != null) { cl = createPathClassLoader(systemServerClasspath, parsedArgs.mTargetSdkVersion); } } } static ClassLoader createPathClassLoader(String classPath, int targetSdkVersion) { String libraryPath = System.getProperty("java.library.path"); // 父加載器是BootClassLoader ClassLoader parent = ClassLoader.getSystemClassLoader().getParent(); // 創建工廠模式創建PathClassLoader return ClassLoaderFactory.createClassLoader(classPath, libraryPath, libraryPath, parent, targetSdkVersion, true /* isNamespaceShared */, null /* classLoaderName */); }}public abstract class ClassLoader { public static ClassLoader getSystemClassLoader() { return SystemClassLoader.loader; } static private class SystemClassLoader { public static ClassLoader loader = ClassLoader.createSystemClassLoader(); } private static ClassLoader createSystemClassLoader() { String classPath = System.getProperty("java.class.path", "."); String librarySearchPath = System.getProperty("java.library.path", ""); // 父加載器是BootClassLoader return new PathClassLoader(classPath, librarySearchPath, BootClassLoader.getInstance()); }}
可見PathClassLoader的父加載器是BootClassLoader。
(3) DexClassLoader
public class DexClassLoader extends BaseDexClassLoader { /** * * @param dexPath : Dex相關文件的路徑 * @param optimizedDirectory: 解壓的dex的存儲路徑 * @param librarySearchPath:包含C/C++庫的路徑集合 * @param parent : 父加載器 */ public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) { super(dexPath, null, librarySearchPath, parent); }}
DexClassLoader也是繼承BaseDexClassLoader,相比PathClassLoader則是可以定義解壓dex的存儲路徑。
除了BootClassLoader、PathClassLoader、DexClassLoader這三個類加載器外還有,InMemoryDexClassLoader:用于加載內存的dex;SecureClassLoader:權限檢查的ClassLoader;URLClassLoader:URL的ClassLoade。
Android中所有的類加載器都繼承于ClassLoader抽象類,這個類的loadClass()方法同樣實現了雙親委托機制。
(1) 雙親委托機制
public abstract class ClassLoader { /** * 雙親委托機制 */ protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // 1. 先檢查class是否已經加載過 Class<?> c = findLoadedClass(name); if (c == null) { // 沒有加載過 try { if (parent != null) { // 先給父ClassLoader加載Class c = parent.loadClass(name, false); } else { // 調用BootClassLoader加載Class c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // 父的ClassLoader都沒有加載class,則調用findClass()給此ClassLoader加載 c = findClass(name); } } return c; } protected Class<?> findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); }}
ClassLoader的loadClass()方法定義了加載器加載類的過程:
那這個findClass()方法在ClassLoader中是一個空實現,它讓給你子類去實現這個查找的過程。那這里以BaseDexClassLoader為例,看findClass()是如何查找class文件的:
public class BaseDexClassLoader extends ClassLoader { private final DexPathList pathList; public BaseDexClassLoader(String dexPath, String librarySearchPath, ClassLoader parent, ClassLoader[] sharedLibraryLoaders, boolean isTrusted) { super(parent); this.sharedLibraryLoaders = sharedLibraryLoaders == null ? null : Arrays.copyOf(sharedLibraryLoaders, sharedLibraryLoaders.length); this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted); reportClassLoaderChain(); } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { // 調用DexPathList.findClass方法查找class Class c = pathList.findClass(name, suppressedExceptions); if (c == null) { ClassNotFoundException cnfe = new ClassNotFoundException( "Didn't find class /"" + name + "/" on path: " + pathList); for (Throwable t : suppressedExceptions) { cnfe.addSuppressed(t); } throw cnfe; } return c; }}
調用DexPathList.findClass()方法去查找class文件:
public final class DexPathList { private Element[] dexElements; DexPathList(ClassLoader definingContext, String dexPath, String librarySearchPath, File optimizedDirectory, boolean isTrusted) { this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory, suppressedExceptions, definingContext, isTrusted); } public Class<?> findClass(String name, List<Throwable> suppressed) { // 遍歷Element數組去查詢 for (Element element : dexElements) { Class<?> clazz = element.findClass(name, definingContext, suppressed); if (clazz != null) { return clazz; } } if (dexElementsSuppressedExceptions != null) { suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions)); } return null; }} /*package*/ static class Element { @UnsupportedAppUsage private final File path; private final Boolean pathIsDirectory; @UnsupportedAppUsage private final DexFile dexFile; private ClassPathURLStreamHandler urlHandler; private boolean initialized; @UnsupportedAppUsage public Element(DexFile dexFile, File dexZipPath) { if (dexFile == null && dexZipPath == null) { throw new NullPointerException("Either dexFile or path must be non-null"); } this.dexFile = dexFile; this.path = dexZipPath; this.pathIsDirectory = (path == null) ? null : path.isDirectory(); } public Class<?> findClass(String name, ClassLoader definingContext, List<Throwable> suppressed) { // 調用DexFile.loadClassBinaryName()方法去查找 return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed) : null; }}public final class DexFile { public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) { return defineClass(name, loader, mCookie, this, suppressed); } private static Class defineClass(String name, ClassLoader loader, Object cookie, DexFile dexFile, List<Throwable> suppressed) { Class result = null; try { result = defineClassNative(name, loader, cookie, dexFile); } ... return result; } private static native Class defineClassNative(String name, ClassLoader loader, Object cookie, DexFile dexFile) }
DexPathList有一個Element[]數組,每個Element有dex文件路徑,通過遍歷Element[]數組,調用Element. loadClassBinaryName()方法去查找是否對應的class文件,最后調用defineClassNative()native方法去查找。
本文鏈接:http://www.tebozhan.com/showinfo-26-16132-0.htmlJVM類加載機制分析
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: 編寫高質量代碼的十條黃金法則