一、什么是dex2oat
Dex2oat (dalvik excutable file to optimized art file) ,是一個對 dex 文件進行編譯優化的程序,在我們的 Android 手機中的位置是 /system/bin/dex2oat,對應的源碼路徑為 android/art/dex2oat/dex2oat.cc,通過編譯優化,可以提升用戶日常的使用體驗(包含安裝速度、啟動速度、應用使用過程中的流暢度等),是 Android Art Runtime 中的一個重要的模塊, 本文我們一起來了解下 dex2oat 的功能以及常用的場景。
二、為什么要進行dex2oat轉換?
眾所周知, Android 虛擬機可以識別的是dex文件,應用使用過程中如果每次將dex文件加載進行內存,解釋性執行字節碼,效率會很低, 嚴重影響用戶體驗。通過dex2oat 優化后, 可以在系統運行之前利用合適的時機將dex文件字節碼提前轉化為虛擬機可以執行運行的機器碼,后續直接從效率更高的機器碼中運行,則運行階段更加流暢,優化用戶體驗。
Dex2oat的主要觸發場景
三、幾種dex2oat 相關的文件
Dex文件:Dex文件是Android 虛擬機識別的一種可執行文件,我們可以解壓一個apk, 獲取其中的class.dex文件, 通過dexdump 命令工具對dex 文件進行解析,查看文件內容,更多格式說明查看參考資料 1。
Oat文件:art執行的文件,dex2oat程序編譯dex文件的產物。我們可以通過oatdump 查看oat文件具體內容。
Odex文件: Optimizied dexfile, dex文件已經dexopt操作優化后的產物,和dex文件類似,使用了一些優化操作碼。
Art文件:Image文件,記錄應用啟動熱點函數相關地址,方便尋址。
Vdex文件:Verified dex,主要包含dex和quicken info信息。Andorid 8.0新增機制產生的文件,其目的主要是為了跳過verified流程,減少dex2oat執行時間。
四、如何使用Dex2oat
4.1 Dex2oat用法
Dex2oat工具的常用參數如下:
4.2 Dex2oat日志解析
從日志中可以看出,在dex2oat發起時具體的編譯類型、線程數以及編譯原因等等。
常見的編譯類型:verify、quicken、space-profile、space、speed-profile、speed、everything, 具體效果從字面上比較好理解, 越后面的類型編譯時間越長,占用的空間也越大,運行時打開速度也越快,典型空間換時間思路的體現,其中profile類型的編譯方式主要是根據JIT運行過程中熱點函數的情況進行編譯,JIT機制不進行展開,可以查閱相關資料。
4.3 和dex2oat相關的系統配置
[pm.dexopt.ab-ota]: [speed-profile]
[pm.dexopt.bg-dexopt]: [speed-profile]
[pm.dexopt.boot]: [verify]
[pm.dexopt.first-boot]: [quicken]
[pm.dexopt.inactive]: [verify]
[pm.dexopt.install]: [speed-profile]
[pm.dexopt.shared]: [speed]
4.4 如何手動發起dex2oat操作
通過以上介紹參數含義后,我們可以在adb shell 下通過命令行方式直接發起dex2oat操作,例如強制編譯微信:
adb shell cmd package compile -m speed-profile -f com.tencent.mm
清除配置文件數據並移除經過編譯的代碼:
adb shell cmd package compile --reset com.tencent.mm
下面對具體調用的流程進行分析。
五、Dex2oat 流程分析
本次分析基於Android Q 代碼。上述觸發場景主要涉及PackageManagerService , 所以從該服務作為入口,分析dex2oat的相關流程。
應用在發起dex2oat時,主要通過PMS中接口調用installd觸發的,相關調用函數performDexOpt ,該函數在上述應用安裝以及啟動的時候都會涉及,所以主要查看下這個函數的調用流程,詳見代碼:
傳入參數DexoptOptions , 可以通過該參數指定編譯包名,編譯類型以及標志,返回編譯是否成功。常見編譯標志位:
接下來的調用流程:
以上是Framework中Dex2oat 的調用流程, 感興趣的同學可以跟蹤代碼查看具體細節。
系統經過installd 的dexopt編譯,通常會利用一些關鍵的日志查看dex2oat相關的信息, 比如計算dex2oat運行耗時以及最終的編譯狀態。
Installd dexopt代碼小結:
1.檢測dexopt classloader context 和相關的flag。
2.解析傳入參數,生成dex2oat 命令, 最后通過RunDex2Oat 執行。
常見的dalvik參數控制屬性值,更多標志位參考詳見參考資料2:
經過上述命令,最終調用到底層libart中相關代碼,下面我們查看dex2oat調用的流程圖, 了解dex2oat的相關流程。
流程圖
Dex2oat邏輯小結:
1. 處理命令行參數;
2. 判斷dex2oat的setup是否完成;
3. 根據是否為image類型,分別調用CompileImage或CompileApp的處理,CompileImage和CompileApp的主要功能邏輯類似,主要通過CompilerDriver對dexfile 進行編譯。
六、Dex2oat常見修改思路
前面介紹了dex2oat一些優化以及相關的流程,雖然能夠提高系統的流暢度,如果在不合適的時機發起,很有可能影響到其他用戶操作,需要針對這一類情況進行修改。
以下是常見的修改思路:
1. 根據場景和負載情況調整dex2oat 編譯參數,如編譯類型,編譯線程數量等。
2. 調整boot.img編譯資源,預加載資源文件列表。
3. 后台並行編譯。在系統空閑或者首次加載dex文件的時候預先觸發dex2oat流程,從而加快后續使用dex文件的速度。
舉例:后台應用安裝導致大量資源被dex2oat占用導致前台進程卡頓
dex2oat 優化后雖然能夠增加應用運行的流暢度, 但是如果在短時間內大量發起則會影響用戶界面操作, 造成負面的影響。所以發現應用是因為后台自動更新時, 則可以限制dex2oat運行的線程數量,盡可能的減少對前面進程的影響。
七、總結
本文從dex2oat日志輸出和使用命令出發,介紹了dex2oat常用的場景以及相關的調用流程,以此為根據簡單討論了常見dex2oat優化方面的思路,希望能起到拋磚引玉的作用,加深讀者對dex2oat的原理流程的了解。
參考資料
1.https://source.android.google.cn/devices/tech/dalvik/dex-format
2.https://source.android.com/devices/tech/dalvik/configure#runtime_configuration
3.https://source.android.com/devices/tech/dalvik
4.https://blog.csdn.net/cosmoslhf/article/details/40380559

“內核工匠”微信公眾號
Linux 內核黑科技 | 技術文章 | 精選教程