本頁討論如何配置 ART 及其編譯選項。此處涉及的主題包括系統映像的預編譯配置、dex2oat 編譯選項以及如何權衡系統分區空間、數據分區空間和性能。
請參閱ART 和 Dalvik 、 Dalvik 可執行格式以及 source.android.com 上的其餘頁面以使用 ART。請參閱在 Android 運行時 (ART) 上驗證應用行為以確保您的應用正常運行。
ART 的工作原理
ART 使用提前 (AOT) 編譯,從 Android 7.0(Nougat 或 N)開始,它使用 AOT、即時 (JIT) 編譯和配置文件引導編譯的混合組合。所有這些編譯模式的組合都是可配置的,將在本節中討論。例如,Pixel 設備配置有以下編譯流程:
- 應用程序最初安裝時沒有任何 AOT 編譯。應用程序運行的前幾次,它將被解釋,而頻繁執行的方法將被 JIT 編譯。
- 當設備處於空閒和充電狀態時,編譯守護程序會運行以根據第一次運行期間生成的配置文件對常用代碼進行 AOT 編譯。
- 應用程序的下一次重新啟動將使用配置文件引導的代碼,並避免在運行時對已編譯的方法進行 JIT 編譯。在新運行期間獲得 JIT 編譯的方法將添加到配置文件中,然後由編譯守護程序拾取。
ART 包含一個編譯器( dex2oat
工具)和一個為啟動 Zygote 而加載的運行時 ( libart.so
)。 dex2oat
工具獲取一個 APK 文件並生成一個或多個運行時加載的編譯工件文件。文件的數量、它們的擴展名和名稱可能會因版本而異,但在 Android O 版本中,正在生成的文件是:
-
.vdex
:包含 APK 的未壓縮 DEX 代碼,以及一些額外的元數據以加快驗證速度。 -
.odex
:包含 APK 中方法的 AOT 編譯代碼。 -
.art (optional)
:包含 APK 中列出的一些字符串和類的 ART 內部表示,用於加速應用程序啟動。
編譯選項
ART 的編譯選項分為兩類:
- 系統 ROM 配置:在構建系統映像時,哪些代碼會被 AOT 編譯。
- 運行時配置:ART 如何在設備上編譯和運行應用程序。
配置這兩個類別的一個核心 ART 選項是編譯器過濾器。編譯器過濾器驅動 ART 如何編譯 DEX 代碼,並且是傳遞給dex2oat
工具的選項。從 Android O 開始,有四種官方支持的過濾器:
- verify :只運行 DEX 代碼驗證。
- quicken :運行 DEX 代碼驗證並優化一些 DEX 指令以獲得更好的解釋器性能。
- speed :運行 DEX 代碼驗證和 AOT 編譯所有方法。
- speed-profile :運行配置文件中列出的 DEX 代碼驗證和 AOT 編譯方法。
系統ROM配置
有許多 ART 構建選項可用於配置系統 ROM。如何配置這些選項取決於/system
的可用存儲空間和預裝應用程序的數量。編譯成系統 ROM 的 JAR/APK 可以分為四類:
- 引導類路徑代碼:默認使用速度編譯器過濾器編譯。
- 系統服務器代碼:默認使用速度編譯器過濾器編譯。
- 產品特定的核心應用程序:默認使用速度編譯器過濾器編譯。
- 所有其他應用程序:默認使用quicken編譯器過濾器編譯。
生成文件選項
WITH_DEXPREOPT
-
DONT_DEXPREOPT_PREBUILTS
(Android L 起) -
PRODUCT_DEX_PREOPT_DEFAULT_COMPILER_FILTER
(自 Android 9 起) -
WITH_DEXPREOPT_BOOT_IMG_AND_SYSTEM_SERVER_ONLY
(Android O MR1 中的新功能) -
LOCAL_DEX_PREOPT
-
PRODUCT_DEX_PREOPT_BOOT_FLAGS
-
PRODUCT_DEX_PREOPT_DEFAULT_FLAGS
-
PRODUCT_DEX_PREOPT_MODULE_CONFIGS
-
PRODUCT_DEXPREOPT_SPEED_APPS (New in Android O)
-
PRODUCT_SYSTEM_SERVER_APPS (New in Android O)
-
PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD(Post Android O)
-
WITH_DEXPREOPT_PIC (Removed in Android O)
-
WITH_DEXPREOPT_BOOT_IMG_ONLY
(在 Android O MR1 中移除)
是否在系統映像上安裝的 DEX 代碼上調用dex2oat
。默認啟用。
啟用DONT_DEXPREOPT_PREBUILTS
可防止預構建被預優化。這些應用包含在其Android.mk
中指定的include $(BUILD_PREBUILT)
,例如 Gmail。跳過可能通過 Google Play 更新的預構建應用程序的預優化可以節省/system
空間,但確實會增加首次啟動時間。
PRODUCT_DEX_PREOPT_DEFAULT_COMPILER_FILTER
指定預優化應用程序的默認編譯器過濾器。這些應用包含在其Android.mk
中指定的include $(BUILD_PREBUILT)
,例如 Gmail。如果未指定,則默認值為 quicken。
啟用WITH_DEXPREOPT_BOOT_IMG_AND_SYSTEM_SERVER_ONLY
僅預優化引導類路徑和系統服務器 jar。
通過在模塊定義中指定LOCAL_DEX_PREOPT
選項,也可以在單個應用程序的基礎上啟用或禁用預優化。這對於禁用可能立即接收 Google Play 更新的應用程序的預優化很有用,因為更新會使系統映像中的預優化代碼過時。這對於節省主要版本升級 OTA 的空間也很有用,因為用戶可能已經在數據分區中擁有較新版本的應用程序。
LOCAL_DEX_PREOPT
支持值 'true' 或 'false' 分別啟用或禁用預優化。此外,如果預優化不應從 APK 或 JAR 文件中刪除classes.dex
文件,則可以指定“nostripping”。通常,此文件會被剝離,因為在預優化後不再需要它,但最後一個選項是允許第三方 APK 簽名保持有效所必需的。
將選項傳遞給dex2oat
以控制引導映像的編譯方式。它可用於指定自定義圖像類列表、編譯類列表和編譯器過濾器。
將選項傳遞給dex2oat
以控制除引導映像之外的所有內容的編譯方式。
提供為特定模塊和產品配置傳遞dex2oat
選項的能力。它由$(call add-product-dex-preopt-module-config,<modules>,<option>)
在產品的device.mk
文件中設置,其中<modules>
是 JAR 和 APK 的 LOCAL_MODULE 和 LOCAL_PACKAGE 名稱列表文件,分別。
已被確定為產品核心並且需要使用速度編譯器過濾器進行編譯的應用程序列表。例如,SystemUI 等持久性應用程序只有在下次重新啟動時才有機會使用配置文件引導式編譯,因此產品最好讓這些應用程序始終進行 AOT 編譯。
系統服務器加載的應用程序列表。這些應用程序將默認使用速度編譯器過濾器進行編譯。
是否在設備上包含 ART 的調試版本。默認情況下,這是為 userdebug 和 eng 構建啟用的。可以通過將選項顯式設置為true或false來覆蓋該行為。
默認情況下,設備將使用非調試版本 ( libart.so )。要切換,請將系統屬性persist.sys.dalvik.vm.lib.2
設置為libartd.so 。
在 Android 5.1.0 到 Android 6.0.1 中,可以指定WITH_DEXPREOPT_PIC
以啟用與位置無關的代碼 (PIC)。這樣,鏡像中的編譯代碼就不必從 /system 重新定位到 /data/dalvik-cache 中,從而節省了數據分區中的空間。但是,會產生輕微的運行時影響,因為它禁用了利用位置相關代碼的優化。通常,想要在 /data 中節省空間的設備應該啟用 PIC 編譯。
在 Android 7.0 中,默認啟用 PIC 編譯。
此選項已替換為 WITH_DEXPREOPT_BOOT_IMG_AND_SYSTEM_SERVER_ONLY,它也預先選擇了系統服務器 jar。
引導類路徑配置
- 預加載的類列表
預加載類列表是 zygote 在啟動時初始化的類列表。這使每個應用程序不必單獨運行這些類初始化程序,從而使它們能夠更快地啟動並共享內存中的頁面。默認情況下,預加載的類列表文件位於frameworks/base/config/preloaded-classes
,它包含一個針對典型手機使用進行了調整的列表。對於可穿戴設備等其他設備,這可能會有所不同,因此必須進行相應調整。調整時要小心;當加載未使用的類時,添加太多類會浪費內存。添加太少的類會迫使每個應用程序必須擁有自己的副本,這又會浪費內存。
示例用法(在產品的 device.mk 中):
PRODUCT_COPY_FILES += <filename>:system/etc/preloaded-classes
注意:必須在繼承任何產品配置 makefile 之前放置此行,這些 makefile 從以下位置獲取默認文件: build/target/product/base.mk
圖像類列表是 dex2oat 提前初始化並存儲在 boot.art 文件中的類列表。這允許 zygote 在啟動時從 boot.art 文件中加載這些結果,而不是在預加載期間為這些類本身運行初始化程序。這樣做的一個關鍵特性是,從映像加載並在進程之間共享的頁面可以是乾淨的,從而允許它們在內存不足的情況下輕鬆換出。在 L 中,默認情況下,圖像類列表使用與預加載類列表相同的列表。從 AOSP 中的 post-L 開始,可以使用以下命令指定自定義圖像類列表:
PRODUCT_DEX_PREOPT_BOOT_FLAGS
使用示例(在產品的device.mk
中):
PRODUCT_DEX_PREOPT_BOOT_FLAGS += --image-classes=<filename>
在 post-L AOSP 中,可以使用已編譯的類列表在預優化期間指定引導類路徑中的類子集進行編譯。對於空間非常緊張且無法容納整個預優化啟動映像的設備,這可能是一個有用的選項。但是,此列表未指定的註釋類將不會被編譯 - 甚至在設備上 - 並且必須被解釋,這可能會影響運行時性能。默認情況下,dex2oat 將在 $OUT/system/etc/compiled-classes 中查找已編譯的類列表,因此可以通過 device.mk 將自定義的類複製到該位置。也可以使用以下命令指定特定文件位置:
PRODUCT_DEX_PREOPT_BOOT_FLAGS
示例用法(在產品的device.mk
中):
PRODUCT_COPY_FILES += <filename>:system/etc/compiled-classes
注意:必須在繼承任何產品配置 makefile 之前放置此行,這些 makefile 從以下位置獲取默認文件: build/target/product/base.mk
運行時配置
即時選項
以下選項僅影響 ART JIT 編譯器可用的 Android 版本。
- dalvik.vm.usejit:是否啟用 JIT。
- dalvik.vm.jitinitialsize(默認64K):代碼緩存的初始容量。代碼緩存將定期 GC 並在需要時增加。
- dalvik.vm.jitmaxsize(默認64M):代碼緩存的最大容量。
- dalvik.vm.jitthreshold:(默認為 10000)- 這是方法的“熱度”計數器需要通過的閾值,以便方法被 JIT 編譯。 “熱度”計數器是運行時內部的指標。它包括調用次數、後向分支和其他因素。
- dalvik.vm.usejitprofiles:是否啟用 JIT 配置文件;即使 dalvik.vm.usejit 為假,也可以使用它。請注意,如果這是 false,則編譯器過濾器speed-profile不會 AOT 編譯任何方法,並且等效於quicken 。
- dalvik.vm.jitprithreadweight(默認為 dalvik.vm.jitthreshold / 20)- 應用程序 UI 線程的 JIT“樣本”(參見 jitthreshold)的權重。用於加快編譯直接影響用戶與應用交互時體驗的方法。
- dalvik.vm.jittransitionweight:(默認為 dalvik.vm.jitthreshold / 10)在編譯代碼和解釋器之間轉換的方法調用的權重。這有助於確保編譯所涉及的方法以最小化轉換(這是昂貴的)。
包管理器選項
從 Android 7.0 開始,有一種通用方法可以指定在各個階段發生的編譯/驗證級別。編譯級別可以通過系統屬性配置,默認值為:
-
pm.dexopt.install=speed-profile
-
pm.dexopt.bg-dexopt=speed-profile
-
pm.dexopt.boot=verify
-
pm.dexopt.first-boot=quicken
設備首次啟動時的編譯過濾器。這裡使用的過濾器只會影響出廠後的開機時間。我們建議加快過濾器的速度,以避免在用戶第一次使用手機之前等待很長時間。請注意,如果
/system
中的所有應用程序都已使用quicken編譯器過濾器編譯或使用speed或speed-profile編譯器過濾器編譯,則pm.dexopt.first-boot
無效。
這是通過 Google Play 安裝應用程序時使用的編譯過濾器。我們建議將安裝過濾器設置為 speed-profile 以啟用 dex 元數據文件中的配置文件。請注意,如果沒有提供配置文件或者它是空的,速度配置文件相當於加速。
這是設備空閒、充電和充滿電時使用的編譯過濾器。嘗試使用速度配置文件編譯器過濾器以利用配置文件引導的編譯並節省存儲空間。
無線更新後使用的編譯過濾器。我們強烈建議為此選項使用驗證編譯器過濾器,以避免很長的啟動時間。
Dex2oat 選項
請注意,這些選項會在設備上編譯和預優化期間影響dex2oat
,而上面討論的大多數選項僅影響預優化。
在編譯啟動映像時控制dex2oat
:
- dalvik.vm.image-dex2oat-Xms:初始堆大小
- dalvik.vm.image-dex2oat-Xmx:最大堆大小
- dalvik.vm.image-dex2oat-filter:編譯器過濾器選項
- dalvik.vm.image-dex2oat-threads:要使用的線程數
要在dex2oat
編譯啟動映像以外的所有內容時控制它:
- dalvik.vm.dex2oat-Xms:初始堆大小
- dalvik.vm.dex2oat-Xmx:最大堆大小
- dalvik.vm.dex2oat-filter:編譯器過濾選項
在 Android 6.0 之前的版本中,提供了一個額外的選項來編譯除啟動映像之外的所有內容:
- dalvik.vm.dex2oat-threads:要使用的線程數
從 Android 6.1 開始,這變成了編譯啟動映像之外的所有內容的兩個附加選項:
- dalvik.vm.boot-dex2oat-threads:啟動時使用的線程數
- dalvik.vm.dex2oat-threads:啟動後使用的線程數
從 Android 7.1 開始,提供了兩個選項來控制在編譯啟動映像以外的所有內容時如何使用內存:
- dalvik.vm.dex2oat-very-large:禁用 AOT 編譯的最小總 dex 文件大小(以字節為單位)
- dalvik.vm.dex2oat-swap:使用 dex2oat 交換文件(用於低內存設備)
不應減少控制dex2oat
的初始和最大堆大小的選項,因為它們可能會限制可以編譯的應用程序。
從 Android 11 開始,提供了三個 CPU 親和性選項,以允許將編譯器線程限制為特定的 CPU 組:
- dalvik.vm.boot-dex2oat-cpu-set:在引導期間運行 dex2oat 線程的 CPU
- dalvik.vm.image-dex2oat-cpu-set:在編譯啟動映像時運行 dex2oat 的 CPU
- dalvik.vm.dex2oat-cpu-set:啟動時間後運行 dex2oat 線程的 CPU
CPU 應指定為以逗號分隔的 CPU id 列表。例如,要在 CPU 0-3 上的 dex2oat 上運行,請設置:
dalvik.vm.dex2oat-cpu-set=0,1,2,3
在設置 CPU 親和性屬性時,我們建議將 dex2oat 線程數的相應屬性與所選的 CPU 數匹配,以避免不必要的內存和 I/O 爭用:
dalvik.vm.dex2oat-cpu-set=0,1,2,3 dalvik.vm.dex2oat-threads=4
從 Android 12 開始,添加了以下選項:
- dalvik.vm.ps-min-first-save-ms:等待運行時生成應用程序配置文件的時間,應用程序第一次啟動的時間
- dalvik.vm.ps-min-save-period-ms:更新應用配置文件之前等待的最短時間
- dalvik.vm.systemservercompilerfilter:設備在重新編譯系統服務器時將使用的編譯器過濾器
A/B 特定配置
ROM 配置
從 Android 7.0 開始,設備可以使用兩個系統分區來啟用A/B 系統更新。為了節省系統分區大小,可以將預先選擇的文件安裝在未使用的第二個系統分區中。然後在首次啟動時將它們複製到數據分區。
示例用法(在device-common.mk
中):
PRODUCT_PACKAGES += \ cppreopts.sh PRODUCT_PROPERTY_OVERRIDES += \ ro.cp_system_other_odex=1
在設備的BoardConfig.mk
:
BOARD_USES_SYSTEM_OTHER_ODEX := true
請注意,引導類路徑代碼、系統服務器代碼和特定於產品的核心應用程序始終編譯到系統分區。默認情況下,所有其他應用程序都編譯到未使用的第二個系統分區。這可以通過SYSTEM_OTHER_ODEX_FILTER
來控制,默認值為:
SYSTEM_OTHER_ODEX_FILTER ?= app/% priv-app/%
後台dexopt OTA
使用啟用 A/B 的設備,可以在後台編譯應用程序以更新到新的系統映像。請參閱後台應用程序編譯以選擇在系統映像中包含編譯腳本和二進製文件。用於此編譯的編譯過濾器通過以下方式控制:
pm.dexopt.ab-ota=speed-profile
我們建議使用speed-profile以利用配置文件引導的編譯並節省存儲空間。