Dvm類加載流程

類加載流程

  • 對dex文件進行驗證並優化,生成odex文件
  • 對odex文件進行解析,解析生成DexFile數據結構,即將文件形式的數據轉換成內存中虛擬機可達的數據
  • 對指定的類進行加載,操作DexFile提取對應類的字節碼,生成ClassObject數據結構

Dexopt

​ Dalivk中,dex的優化使用的是dexopt,將dex文件優化爲Odex文件,最終提交給下一步的加載過程。Odex文件的本質只是在原dex文件的基礎上進行優化,並生成.Odex文件進行存儲,以提高dalvik虛擬機運行的高效性和安全性。dex的優化過程都是在一個新進程中進行的。

Dexopt主要流程:

  • 建立dex的類索引表——使虛擬機快速find dex中某個類的地址
  • 寄存器的內存映射——減少odex->DexFile的內存映射操作
  • 添加依賴庫信息——添加dex需要使用的本地函數庫
  • dex中字節碼的替換——比如類似編譯中的內聯優化

Dex和Odex文件結構對比圖

應用安裝到dexopt:

PackageManagerService是用來管理應用安裝,卸載,優化等工作的系統服務。和PMS的各種操作最終會通過Java層的Installer—>InstallerConnection—>Socket通信到native的installd.c服務

InstallerConnection.connect():

InstallerConnection.dexopt():

 

最終會調用到dalvik/dexopt/OptMain.cpp

 

Dex文件的解析

虛擬機需要訪問到可讀的Dex數據來進行類加載。因此我們需要將dex文件解析成DexFile的內存中的數據結構。其解析過程實則是將DexFile數據結構中的各個成員變量與Dex文件的各個數據部分相關聯。

DexFile的結構體:

需要注意的是這一步是在Dex文件優化之後,所以從這裏開始提到的Dex文件都是Odex文件。具體的解析過程不敘述。接下來的過程就是要從DexFile中加載指定的類,並將其裝入虛擬機的運行時環境中。

運行時數據裝載

​ 到這裏,我們需要抽象出另一個數據結構——ClassObject。我們到這裏可以梳理一下流程:

 

Dex的加載一般通過DexClassLoder和PathClassLoder。區別就在於前者可以加載任意路徑下的.jar或者.apk,而後者只能加載默認路徑下的dex文件,即/data/dalvik-cache。然後兩者都繼承自BaseDexClassLoader。

後者的構造函數不能設置Odex文件的路徑,因此一般用作系統類(其實最終是BootClassLoader加載的)和應用類,前者可以動態的設置Odex路徑。

真正的加載函數都是調用自BaseDexClassLoader的父類ClassLoader的loadClass函數:

 

先調用了findLoadedClass()方法:

 

先判斷虛擬機是否已經加載了這個class,如果加載了就會直接返回。

再看到後面,先調用了parent的loadClass()函數,如果爲空纔會調用自己的findClass()函數。雙親委派機制(責任鏈)以及模版方法的設計模式。Android建議我們不要重寫loadClass()方法,去重寫findClass()方法,就是爲了遵循這個機制和生態。因此我們繼續跟蹤BaseDexClassLoader的findClass()方法:

看到會調用DexPathList的findClass()方法,而這個DexList其實內部維護着一個DexFile的集合。繼續跟蹤:

遍歷DexFile集合,然後去輪詢Class。

進入vm\native\dalivk_system_DexFile.cpp中的Dalvik_dalvik_system_DexFile_defineClass

調用dvmGetRawDexFileDex或者dvmGetJarFileDex(如果是jar包)方法去給指向DexFile的指針賦值(其實DexFile是DvmDex的一個成員變量),然後將這個指針傳遞進dvmDefineClass()函數,而這個函數最終調用了findClassNoInit()函數:

判斷是否已經加載,如果沒有加載會繼續進行加載,通過dexFindClass()方法,返回一個DexClassDef數據結構,這個數據結構是爲了方便快速定位類在Dex中的位置,然後最終通過loadClassFromDex()方法給ClassObject指針賦值:

loadClassFromDex()方法流程大致如下:

  • 爲ClassObject申請內存
  • 設置字段信息
  • 爲超類建立索引
  • 加載類接口
  • 加載類字段
  • 加載類方法

以上爲類加載的加載階段。後面會對ClassObject進行進一步的加工,後面緊接着調用了dvmLinkClass()方法進行Prepare and resolve,主要將符號引用轉換成爲直接引用,在其中會進一步調用dvmResolveClass()方法,而這個方法實際上是在解析當前被加載類的父類以及接口:

加載完了之後,就會將這個符號引用轉換爲直接引用,並對GC可見。

/dalvik/vm/oo/Resolve.cpp

對於非基本類型(int、long…),調用dvmFindClassNoInit方法進行加載。如果是基本類型調用dvmFindPrimitiveClass。

/dalvik/vm/oo/Class.cpp

一般加載的非系統類,loader不爲NULL,調用findClassFromLoaderNoInit方法進行加載。實際是調用loader的loadClass方法來加載類的。

 

可以看到dvmResolveClass其實也是加載類,而殼基本上都是hook該函數,大概是因爲加載類都會執行這個函數,而且虛擬機解釋器,比如const-class指令的實現就會調用dvmResolveClass。

更詳細的細節可以參考https://blog.csdn.net/beyond702/article/details/50681453

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章