上圖是Android整體的架構(gòu)障般,Android Runtime之于Android而言相當(dāng)于心臟之于人體盛杰,是Android程序加載和運(yùn)行的環(huán)境即供。這篇文章主要針對Android Runtime部分進(jìn)行展開,探討Android Runtime的發(fā)展以及目前現(xiàn)狀办素,并介紹應(yīng)用Profile-Guided Optimization(PGO)技術(shù)對應(yīng)用啟動(dòng)速度進(jìn)行優(yōu)化的可行性性穿。轉(zhuǎn)載請注明來源「申國駿」
App運(yùn)行時(shí)演進(jìn)
JVM
Android原生代碼使用Java或者Kotlin編寫雷滚,這些代碼會(huì)通過javac或者kotlinc編譯成.class文件祈远,在Android之前车份,這些.class文件會(huì)被輸入到JVM中執(zhí)行。JVM可以簡單分為三個(gè)子系統(tǒng)出爹,分別是Class Loader严就、Runtime Data Area以及Execution Engine梢为。其中Class Loader主要負(fù)責(zé)加載類、校驗(yàn)字節(jié)碼祟印、符號引用鏈接及對靜態(tài)變量和靜態(tài)方法分配內(nèi)存并初始化旁理。Runtime Data負(fù)責(zé)存儲(chǔ)數(shù)據(jù)我磁,分為方法區(qū)夺艰、堆區(qū)郁副、棧區(qū)存谎、程序計(jì)數(shù)器以及本地方法棧肥隆。Execution Engine負(fù)責(zé)二進(jìn)制代碼的執(zhí)行以及垃圾回收栋艳。
Execution Engine中吸占,會(huì)采用Interpreter或者JIT執(zhí)行矾屯。其中Interpreter表示在運(yùn)行的過程中對二進(jìn)制代碼進(jìn)行解釋,每次執(zhí)行相同的二進(jìn)制代碼都進(jìn)行解釋比較浪費(fèi)資源孙技,因此對于熱區(qū)的二進(jìn)制代碼會(huì)進(jìn)行JIT即時(shí)編譯绪杏,對二進(jìn)制代碼編譯成機(jī)器碼纽绍,這樣相同的二進(jìn)制代碼執(zhí)行時(shí)拌夏,就不用再次進(jìn)行解釋履因。
DVM(Android 2.1/2.2)
JVM是stack-based的運(yùn)行環(huán)境栅迄,在移動(dòng)設(shè)備中對性能和存儲(chǔ)空間要求較高毅舆,因此Android使用了register-based的Dalvik VM憋活。從JVM轉(zhuǎn)換到DVM我們需要將.class文件轉(zhuǎn)換為.dex文件虱黄,從.class轉(zhuǎn)換到.dex的過程需要經(jīng)過 desugar -> proguard -> dex compiler三個(gè)過程橱乱,這三個(gè)過程后來逐步變成 proguard -> D8(Desugar) 直到演變到今天只需要一步R8(D8(Desugar))。
我們主要關(guān)注Android中Runtime Engine與JVM的區(qū)別作瞄。在Android早期的版本里面,只存在Interpreter解釋器粉洼,到了Android2.2版本將JIT引入属韧,這個(gè)版本Dalvik與JVM的Runtime Engine區(qū)別不大宵喂。
ART-AOT(Android 4.4/5.0)
為了加快應(yīng)用的啟動(dòng)速度和體驗(yàn)锅棕,到了Android4.4淌山,Google提供了一個(gè)新的運(yùn)行時(shí)環(huán)境ART(Android Runtime)泼疑,到了Android5.0,ART替換Dalvik成為唯一的運(yùn)行時(shí)環(huán)境移稳。
ART運(yùn)行時(shí)環(huán)境中个粱,采用了AOT(Ahead-of-time)編譯方式都许,即在應(yīng)用安裝的時(shí)候就將.dex提前編譯成機(jī)器碼,經(jīng)過AOT編譯之后.dex文件會(huì)生成.oat文件颖低。這樣在應(yīng)用啟動(dòng)執(zhí)行的時(shí)候,因?yàn)椴恍枰M(jìn)行解釋編譯蹬敲,大大加快了啟動(dòng)速度伴嗡。
然而AOT帶來了以下兩個(gè)問題:
- 應(yīng)用安裝時(shí)間大幅增加瘪校,由于在安裝的過程中同時(shí)需要編譯成機(jī)器碼,應(yīng)用安裝時(shí)間會(huì)比較長泣懊,特別在系統(tǒng)升級的時(shí)候馍刮,需要對所有應(yīng)用進(jìn)行重新編譯窃蹋,出現(xiàn)了經(jīng)典的升級等待噩夢警没。
- 應(yīng)用占用過多的存儲(chǔ)空間,由于所有應(yīng)用都被編譯成.oat機(jī)器碼亡脸,應(yīng)用所占的存儲(chǔ)空間大大增加梗掰,使得本來并不充裕的存儲(chǔ)空間變得雪上加霜。
進(jìn)一步思考對應(yīng)用全量進(jìn)行編譯可能是沒有必要的摧茴,因?yàn)橛脩艨赡苤粫?huì)用到一個(gè)應(yīng)用的部分常用功能苛白,并且全量編譯之后更大的機(jī)器碼加載會(huì)占用IO資源焚虱。
ART-PGO(Android 7.0)
從Android7.0開始鹃栽,Google重新引入了JIT的編譯方式民鼓,不再對應(yīng)用進(jìn)行全量編譯,結(jié)合AOT夯到、JIT耍贾、Interpreter三者的優(yōu)勢提出了PGO(Profile-guided optimization)的編譯方式荐开。
在應(yīng)用執(zhí)行的過程中劝赔,先使用Interpreter直接解釋着帽,當(dāng)某些二進(jìn)制代碼被調(diào)用次數(shù)較多時(shí)仍翰,會(huì)生成一個(gè)Profile文件記錄這些方法存儲(chǔ)起來予借,當(dāng)二進(jìn)制代碼被頻繁調(diào)用時(shí)频蛔,則直接進(jìn)行JIT即時(shí)編譯并緩存起來晦溪。
當(dāng)應(yīng)用處于空閑(屏幕關(guān)閉且充電)的狀態(tài)時(shí)三圆,編譯守護(hù)進(jìn)程會(huì)根據(jù)Profile文件進(jìn)行AOT編譯舟肉。
當(dāng)應(yīng)用重新打開時(shí),進(jìn)行過JIT和AOT編譯的代碼可以直接執(zhí)行查库。
這樣就可以在應(yīng)用安裝速度以及應(yīng)用打開速度之間取得平衡路媚。
JIT 工作流程:
ART-Cloud Profile(Android 9.0)
不過這里還是有一個(gè)問題,就是當(dāng)用戶第一次安裝應(yīng)用的時(shí)候并沒有進(jìn)行任何的AOT優(yōu)化樊销,通常會(huì)經(jīng)過用戶多次的使用才能使得啟動(dòng)速度得到優(yōu)化整慎。
考慮到一個(gè)應(yīng)用通常會(huì)有一些用戶經(jīng)常使用執(zhí)行的代碼(例如啟動(dòng)部分以及用戶常用功能)并且大多數(shù)時(shí)候會(huì)有先行版本用于收集Profile數(shù)據(jù),因此Google考慮將用戶生成的Profile文件上傳到Google Play中现柠,并在應(yīng)用安裝時(shí)同時(shí)帶上這個(gè)Profile文件院领,在安裝的過程中,會(huì)根據(jù)這個(gè)Profile對應(yīng)用進(jìn)行部分的AOT編譯够吩。這樣當(dāng)用戶安裝完第一次打開的時(shí)候周循,就能達(dá)到較快的啟動(dòng)速度。
Profile in cloude 需要系統(tǒng)應(yīng)用市場支持,在國內(nèi)市場使用Google Play的占比非常低临扮,因此cloud profile的優(yōu)化在國內(nèi)幾乎是沒有作用的,不過Profile的機(jī)制提供了一個(gè)可以做啟動(dòng)優(yōu)化的思路蚜退。早在2019年蚂且,支付寶就在秒開技術(shù)的回應(yīng)的里面提到過profile-based compile的技術(shù)杏死,參考:如何看待今日頭條自媒體發(fā)布謠言稱「支付寶幾乎秒開是因?yàn)椴捎萌A為方舟編譯器」?窒舟,這也是我們一直研究Profile技術(shù)的原因。困擾著我們的一直有兩個(gè)問題洁墙,第一個(gè)問題是如何生成Profile文件,第二個(gè)問題是怎么使用生成的Profile文件孝扛。對于第一個(gè)問題的解決相對還是有思路的,因?yàn)閍pp運(yùn)行就會(huì)生成profile文件陌选,因此我們手動(dòng)運(yùn)行幾次app就能在文件系統(tǒng)中收集到這個(gè)文件,不過如何以一種較為自動(dòng)化的手段收集仍然是個(gè)問題臼勉。第二個(gè)問題我們知道Profile文件最終生成的位置囱晴,因此我們可以把生成的文件放到相應(yīng)的系統(tǒng)目錄畸写,不過大多數(shù)手機(jī)和應(yīng)用都沒有權(quán)限直接放置這個(gè)文件。因此Profile優(yōu)化技術(shù)一直都沒有落地千所,直到Baseline Proflie讓我們看到了希望。
Baseline Profile
Baseline Profile是一套生成和使用Profile文件的工具待错,在2022年一月份開始進(jìn)入視野,隨后在Google I/O 2022隨著Jetpack新變化得到廣泛關(guān)注瓜客。其背景是Google Map加快了發(fā)版速度,Cloud Profle還沒完全收集好就上新版,導(dǎo)致Cloud Proflie失效。還有一個(gè)背景是Jetpack Compose 不是系統(tǒng)代碼称杨,因此沒有完全編譯成機(jī)器碼,而且Jetpack Compose庫比較大笨奠,因此在Profile生成之前使用了Jetpack Compose的應(yīng)用啟動(dòng)會(huì)產(chǎn)生性能問題赂弓。最后Google為了解決這些問題乡范,創(chuàng)造了收集Profile的BaselineProfileRule Macrobenchmark以及使用Profile的ProfileInstaller。
使用Baseline Profile的機(jī)制可以在Android7及以上的手機(jī)上得到應(yīng)用的啟動(dòng)加速瓶佳,因?yàn)閺纳鲜鲋繟ndroid7就已經(jīng)開始有PGO(Profile-guided optimization)的編譯方式。生成的Profile文件會(huì)打包到apk里面贴彼,并且會(huì)結(jié)合Google Play的Cloud Profile來引導(dǎo)AOT編譯器仗。雖然在國內(nèi)基本上用不了Cloud Profile剃斧,不過Baseline Profile是可以獨(dú)立于Google Play單獨(dú)使用的。
在使用了Baseline Proflie之后,有道詞典的啟動(dòng)速度從線上統(tǒng)計(jì)上看球散,冷啟動(dòng)時(shí)間有15%的提升。
這篇文章主要介紹了Android Runtime的演進(jìn)以及對于應(yīng)用啟動(dòng)的影響泻蚊,下一篇文章我會(huì)詳細(xì)介紹關(guān)于Profile&dex文件優(yōu)化羹奉、Baseline Profile工具庫原理,以及在實(shí)際操作上如何使用的問題细卧,敬請大家期待一下翰苫!
參考
Improving app performance with ART optimizing profiles in the cloud
Understanding Android Runtime (ART) for faster apps (Google I/O'19)
Performance and memory improvements in Android Run Time (ART) (Google I/O '17)
Deep dive into ART(Android Runtime) for dynamic binary analysis | SungHyoun Song | Nullcon 2021
Android 強(qiáng)推的 Baseline Profiles 國內(nèi)能用嗎撩匕?我找 Google 工程師求證了!