java類加載機制

在看java和android的類加載機制,途中有一些疑惑,就先記下來。html

一些概念的理解

jdk和jre是什麼區別? JDK就是Java Development Kit.簡單的說JDK是面向開發人員使用的SDK,它提供了Java的開發環境和運行環境。SDK是Software Development Kit 通常指軟件開發包,能夠包括函數庫、編譯程序等。JRE是Java Runtime Enviroment是指Java的運行環境,是面向Java程序的使用者,而不是開發者。java

rt.jar、dt.jar、tools.jar是什麼? rt.jar這個文件是極爲重要的一個文件,rt是runtime的縮寫,即運行時的意思。是java程序在運行時必不可少的文件。裏面包含了java程序員經常使用的包,如java.lang,java.util,java.io,java.net,java.applet等;dt.jar是關於運行環境的類庫,主要是swing的包,你要用到swing時最好加上;tools.jar 是系統用來編譯一個類的時候用到的,也就是javac的時候用到 。android

lib/jre/lib的區別是什麼? JDK下的lib包括java開發環境的jar包,是給JDK用的,例如JDK下有一些工具,可能要用該目錄中的文件。例如,編譯器等;JDK下的JRE下的lib是開發環境中,運行時須要的jar包。最典型的就是導入的外部驅動jar包。程序員

什麼是android dex文件 明白什麼是 Dex 文件以前,要先了解一下 JVM,Dalvik 和 ART。JVM 是 JAVA 虛擬機,用來運行 JAVA 字節碼程序。Dalvik 是 Google 設計的用於 Android平臺的運行時環境,適合移動環境下內存和處理器速度有限的系統。ART 即 Android Runtime,是 Google 爲了替換 Dalvik 設計的新 Android 運行時環境,在Android 4.4推出。ART 比 Dalvik 的性能更好。Android 程序通常使用 Java 語言開發,可是 Dalvik 虛擬機並不支持直接執行 JAVA 字節碼,因此會對編譯生成的 .class 文件進行翻譯、重構、解釋、壓縮等處理,這個處理過程是由 dx 進行處理,處理完成後生成的產物會以 .dex 結尾,稱爲 Dex 文件。Dex 文件格式是專爲 Dalvik 設計的一種壓縮格式。因此能夠簡單的理解爲:Dex 文件是不少 .class 文件處理後的產物,最終能夠在 Android 運行時環境執行。bootstrap

jvm中的類加載器

啓動類加載器(Bootstrap Class Loader)

主要負責加載jdk中的核心類庫,好比rt.jar和其它在jre/lib中的核心類。Bootstrap Class Loader是全部classloader的父加載器。它是有native代碼實現的api

It’s mainly responsible for loading JDK internal classes, typically rt.jar and other core libraries located in $JAVA_HOME/jre/lib directory. Additionally, Bootstrap class loader serves as a parent of all the other ClassLoader instances.app

This bootstrap class loader is part of the core JVM and is written in native code. Different platforms might have different implementations of this particular class loader.jvm

擴展類加載器(Extension Class Loader)

擴展類加載器是由Sun的ExtClassLoader(sun.misc.Launcher$ExtClassLoader)實現的,它負責將 <JAVA_HOME >/lib/ext或者由系統變量-Djava.ext.dir指定位置中的類庫 加載到內存中。開發者能夠直接使用標準擴展類加載器。函數

系統類加載器(System Class Loader)

系統類加載器是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)實現的,它負責將用戶類路徑(java -classpath或-Djava.class.path變量所指的目錄,即當前類所在路徑及其引用的第三方類庫的路徑,加載到內存中。開發者能夠直接使用系統類加載器。工具

The system or application class loader, on the other hand, takes care of loading all the application level classes into the JVM. It loads files found in the classpath environment variable, -classpath or -cp command line option. Also, it’s a child of Extensions classloader.

The extension class loader is a child of the bootstrap class loader and takes care of loading the extensions of the standard core Java classes so that it’s available to all applications running on the platform.

Extension class loader loads from the JDK extensions directory, usually $JAVA_HOME/lib/ext directory or any other directory mentioned in the java.ext.dirs system property.

舉個例子

public void printClassLoaders() throws ClassNotFoundException {
 
    System.out.println("Classloader of this class:"
        + PrintClassLoader.class.getClassLoader());
 
    System.out.println("Classloader of Logging:"
        + Logging.class.getClassLoader());
 
    System.out.println("Classloader of ArrayList:"
        + ArrayList.class.getClassLoader());
}
複製代碼

運行結果是

Class loader of this class:sun.misc.Launcher$AppClassLoader@18b4aac2

Class loader of Logging:sun.misc.Launcher$ExtClassLoader@3caeaf62

Class loader of ArrayList:null

classloader的類的繼承關係

classDiagram
Object <|-- ClassLoader
ClassLoader <|-- SecureClassLoader
SecureClassLoader <|-- URLClassLoader
URLClassLoader <|-- ExtClassLoader
URLClassLoader <|-- AppClassLoader
BootStrapClassLoader <-- ExtClassLoader
ExtClassLoader <-- AppClassLoader
AppClassLoader <-- ClassLoaderA
AppClassLoader <-- ClassLoaderB
複製代碼

其中,ExtClassLoader屬於Extension Class Loader,AppClassLoader屬於System Class Loader。

雙親委派機制

JVM在加載類時默認採用的是雙親委派機制。通俗的講,就是某個特定的類加載器在接到加載類的請求時,首先將加載任務委託給父類加載器,依次遞歸 (本質上就是loadClass函數的遞歸調用)。所以,全部的加載請求最終都應該傳送到頂層的啓動類加載器中。若是父類加載器能夠完成這個類加載請求,就成功返回;只有當父類加載器沒法完成此加載請求時,子加載器纔會嘗試本身去加載。事實上,大多數狀況下,越基礎的類由越上層的加載器進行加載。

加載過程以下:

  1. 源 ClassLoader 先判斷該 Class 是否已加載,若是已加載,則直接返回 Class,若是沒有則委託給父類加載器。
  2. 父類加載器判斷是否加載過該 Class,若是已加載,則直接返回 Class,若是沒有則委託給祖父類加載器。
  3. 依此類推,直到始祖類加載器(引用類加載器)。
  4. 始祖類加載器判斷是否加載過該 Class,若是已加載,則直接返回 Class,若是沒有則嘗試從其對應的類路徑下尋找 class 字節碼文件並載入。若是載入成功,則直接返回 Class,若是載入失敗,則委託給始祖類加載器的子類加載器。
  5. 始祖類加載器的子類加載器嘗試從其對應的類路徑下尋找 class 字節碼文件並載入。若是載入成功,則直接返回 Class,若是載入失敗,則委託給始祖類加載器的孫類加載器。
  6. 依此類推,直到源 ClassLoader。
  7. 源 ClassLoader 嘗試從其對應的類路徑下尋找 class 字節碼文件並載入。若是載入成功,則直接返回 Class,若是載入失敗,源 ClassLoader 不會再委託其子類加載器,而是拋出異常。

android的class loader

classDiagram
Object <|-- ClassLoader
ClassLoader <|-- BaseDexClassLoader
ClassLoader <|-- SecureClassLoader
SecureClassLoader <|-- URLClassLoader
BaseDexClassLoader <|-- PathClassLoader
BaseDexClassLoader <|-- DexClassLoader
BaseDexClassLoader <|-- InMemoryDexClassLoader
ClassLoader <|-- BootClassLoader
BootClassLoader <-- PathClassLoader
複製代碼

幾個知識點

  1. 在 Android 中,App 安裝到手機後,apk 裏面的 class.dex 中的 class 均是經過 PathClassLoader 來加載的。
  2. 對比 PathClassLoader 只能加載已經安裝應用的 dex 或 apk 文件,DexClassLoader 則沒有此限制,能夠從 SD 卡上加載包含 class.dex 的 .jar 和 .apk 文件,這也是插件化和熱修復的基礎,在不須要安裝應用的狀況下,完成須要使用的 dex 的加載。A class loader that loads classes from .jar and .apk filescontaining a classes.dex entry. This can be used to execute code notinstalled as part of an application.
  3. SecureClassLoader和URLClassLoader和JDK8中的是同樣的
  4. InMemoryDexClassLoader是Android8.0新增的類加載器,繼承自BaseDexClassLoader,用於加載內存中的dex文件。
  5. BootClassLoader是在Zygote進程的入口方法中建立的,PathClassLoader則是在Zygote進程建立SystemServer進程時建立的。

疑惑

SystemServer進程中建立了PathClassLoader,Zygote進程中建立了BootClassLoader,由於app進程是靠Zygote進程fork出來的,那麼app進程中的PathClassLoader是在哪裏建立的?

因而帶着這個問題又研究了一下android api-level 爲28的源代碼,把app中的PathClassLoader的建立過程用時序圖描述出來。

在ActivityThread中ApplicationThread的bindApplication方法中發送消息BIND_APPLICATION給H,H也是ActivityThread的內部類,它繼承了Handler,而後H調用ActivityThread的handleBindApplicationf方法,而後ActivityThread處理該消息時通過層層調用,最後返回了PathClassLoader。

那麼ActivityThread中的ApplicationThread的bindApplication是在何時被誰調用的呢?調用關係以下描述:

能夠看到ActivityThread有個靜態main方法,它通過層層調用而且通過IActivityManager這個IPC操做,調用到了AMS的一些方法,而後AMS再通過IApplicationThread這個IPC操做,調用到了ActivityThread的ApplicationThread的bindApplication方法。

IActivityManager對應的是AMS,IApplicationThread對應的是ActivityThread的ApplicationThread。

ActivityThread的這個靜態main方法是Zygote fork出來app這個子進程後,子進程調用到的。

參考

相關文章
相關標籤/搜索