dex2oat與應用安裝時間優化
背景
- 4.4之前,android使用dalvik虛拟機,采用JIT(Just-in-time 即時編譯),在運作時将位元組碼即時翻譯成機器碼再執行
- 5.0開始,android使用art虛拟機,采用AOT(Ahead Of Time 運作前編譯),在安裝時将位元組碼(.dex)翻譯成機器碼(.oat)再執行,提高運作時效率
- 由于dex2oat過程涉及 讀取dex -> 以類為粒度編譯 -> 生成許多中間檔案 ->合并為.oat(odex)檔案 是以EMMC性能 ,CPU性能, swap區大小, 等硬體性能在一定程度上決定了編譯的速度, 而3561平台性能較弱,編譯時間的增加直接的造成安裝時間提高, 同時應用的熱更新包編譯會占據一定的cpu資源造成系統卡頓,直接影響使用者體驗,本文主要從靈活修改dex2oat排程政策來優化dex2oat過程,而沒有減少dex2oat時間
優化
1.對與安裝應用,根據應用的方法數來決定是否執行dex2oat
- 如果對所有應用強制不進行dex2oat,能一勞永逸,将安裝時間壓縮到最低,但是這違背了art虛拟機設計的初衷
-
雖然很多市場上的app體積比較大,但是除去資源檔案,他的類數量和方法數量不一定像體積那麼大,對于方法數,
較小的應用,去做dex2oat的優化是有必要的,因為與dex2oat時間産生直接關系的是方法數而不是apk體積
- 我們希望在3561平台上将所有應用的安裝時間控制在1分鐘以内,經過測試和統計,執行dex2oat在一分鐘左右的應用方法數在110000左右,是以對于方法數在110000以下的應用執行dex2oat,對方法數在110000以上的應用禁止dex2oat,以達到性能和安裝時間均衡的狀态
commands.cpp
# 從property中擷取dex2oat方法數邊界值
bool have_dex2oat_num_dex_methods_flag = property_get("fly.dex2oat.num_dex_methods",
dex2oat_num_dex_methods_flag, NULL) > 0;
# 設定--num-dex-methods參數,
if(have_dex2oat_num_dex_methods_flag){
sprintf(dex2oat_num_dex_method_arg, "--num-dex-methods=%s", dex2oat_num_dex_methods_flag);
}
dex2oat.cc
if (!image_ &&
compiler_options_->IsCompilationEnabled()) {
size_t num_methods = 0;
for (size_t i = 0; i != dex_files_.size(); ++i) {
const DexFile* dex_file = dex_files_[i];
CHECK(dex_file != nullptr);
num_methods += dex_file->NumMethodIds();
}
# GetNumDexMethodsThreshold()擷取--num-dex-methods參數
if (num_methods != 0
&& compiler_options_->GetNumDexMethodsThreshold() != 0
&& num_methods <= compiler_options_->GetNumDexMethodsThreshold()) {
# 方法數小于--num-dex-methods則設定為kSpeed,執行dex2oat
compiler_options_->SetCompilerFilter(CompilerOptions::kSpeed);
}
else{
# 方法數大于--num-dex-methods則設定為kInterpretOnly,禁止dex2oat
compiler_options_->SetCompilerFilter(CompilerOptions::kInterpretOnly);
}
}
2.對與應用插件,限制對插件執行dex2oat的線程數
熱更新不可能直接把java源碼下發,隻能下發dex檔案,在本地把基線dex給反編譯然後合并再回編譯很明顯不可能,畢竟baksmali.jar和smali.jar加起來都快5m了,熱更新架構不可能搞這麼大。但是利用dex2oat的這個特性,我們可以隻下發包括更新代碼的dex,用戶端接收後把新dex作為classes.dex,舊dex作為classes2.dex,送進dex2oat,就能得到一個更新過代碼的oat檔案,盡可能簡單的完成熱更新。
作者:琴梨梨
連結:https://www.jianshu.com/p/cf63266cca86
插件下發時,dex2oat預設會開啟和cpu核心數相同的線程數去編譯,占據非常的的cpu資源,很容易會造成系統卡頓
插件一般會下載下傳到APP的私有資料目錄,即 “data/data/” 或 “data/user/” ,而使用dex2oat時會把dex的源路徑作為參數傳入,根據參數有無包含這兩個路徑可以判斷是否是一個插件在調用dex2oat,如果是的話則限制dex2oat的線程數為1,保證熱更新的正常和系統的流暢
dex2oat.cc
for (const char* dex_file_name : dex_filenames_) {
std::string fileNameStr(dex_file_name);
if(fileNameStr.find("data/user/") != std::string::npos || fileNameStr.find("data/data/") != std::string::npos) {
compiler_options_->SetCompilerFilter(CompilerOptions::kSpeed);
# 限制線程數為1
thread_count_ = 1;
break;
}
}