在 Android 虛擬機(jī)(ART)中,ArtMethod、OatFile::OatMethod 和 ClassLinker 是三個(gè)核心組件,它們在方法管理、代碼執(zhí)行和類加載過程中扮演著重要角色。本文將深入探討它們的工作原理及其在 ART 中的作用。
1. ArtMethod:Java 方法的內(nèi)部表示
ArtMethod 是 ART 虛擬機(jī)中表示 Java 方法的內(nèi)部結(jié)構(gòu)。它包含了方法的元數(shù)據(jù)和執(zhí)行信息,例如:
- 方法名和簽名:標(biāo)識方法的名稱和參數(shù)。
-
訪問標(biāo)志:如
public、private等。 - 字節(jié)碼或本地代碼指針:指向方法的可執(zhí)行代碼。
- 所屬類:方法所屬的類。
ArtMethod 在方法調(diào)用時(shí)用于查找和執(zhí)行代碼。它的一個(gè)重要字段是 Entry Point(入口點(diǎn)),指向方法的可執(zhí)行代碼入口。根據(jù)方法的編譯狀態(tài),入口點(diǎn)可能是:
- 解釋執(zhí)行:指向解釋器的代碼。
- AOT 編譯:指向 AOT 編譯生成的本地機(jī)器碼。
- JIT 編譯:指向 JIT 編譯生成的本地機(jī)器碼。
2. OatFile::OatMethod:AOT 編譯方法的存儲(chǔ)格式
OatFile::OatMethod 是 ART 中存儲(chǔ)已編譯方法的格式。它包含以下信息:
- 編譯后的本地代碼:AOT 編譯生成的機(jī)器碼。
- 元數(shù)據(jù):如調(diào)試信息和內(nèi)聯(lián)緩存。
OatFile 是存儲(chǔ) AOT 編譯結(jié)果的容器,而 OatMethod 則是其中表示單個(gè)方法的結(jié)構(gòu)。在 AOT 編譯模式下,ArtMethod 的 Entry Point 會(huì)指向 OatFile::OatMethod 中存儲(chǔ)的本地代碼。
3. ClassLinker:類的加載、鏈接和初始化
ClassLinker 負(fù)責(zé)類的加載、鏈接和初始化。它的主要功能包括:
- 類加載:從 DEX 文件加載類定義。
- 類鏈接:解析類依賴,準(zhǔn)備方法表和靜態(tài)字段。
- 類初始化:執(zhí)行類的靜態(tài)初始化塊。
在類加載時(shí),ClassLinker 會(huì)從 DEX 文件加載類和方法。如果存在 AOT 編譯文件(OAT 文件),ClassLinker 會(huì)從 OatFile 中加載 OatMethod,并將其入口點(diǎn)信息設(shè)置到 ArtMethod 的 Entry Point 字段。
4. LinkCode 函數(shù):設(shè)置 ArtMethod 的 Entry Point
LinkCode 函數(shù)是設(shè)置 ArtMethod 的 Entry Point 的關(guān)鍵邏輯之一。以下是它的主要工作流程:
- 檢查方法是否可鏈接:如果方法不可調(diào)用(如抽象方法),則設(shè)置一個(gè)拋出異常的入口點(diǎn)。
-
從 OatFile::OatMethod 獲取代碼:如果存在
oat_class,則從OatFile::OatMethod中獲取 AOT 編譯的本地代碼(quick_code)。 -
設(shè)置方法的 Entry Point:調(diào)用
InitializeMethodsCode將quick_code設(shè)置為ArtMethod的EntryPointFromQuickCompiledCode。 -
處理 Native 方法:對于 Native 方法,設(shè)置 JNI 調(diào)用的入口點(diǎn)(
EntryPointFromJni)。
5. Android 虛擬機(jī)的三種執(zhí)行方式
Android 虛擬機(jī)支持三種主要的程序執(zhí)行方式:解釋執(zhí)行(Interpretation)、即時(shí)編譯(JIT,Just-In-Time) 和 預(yù)先編譯(AOT,Ahead-Of-Time)。以下是它們的詳細(xì)說明和比較:
5.1 解釋執(zhí)行(Interpretation)
- 工作原理:解釋器逐條讀取字節(jié)碼,并動(dòng)態(tài)轉(zhuǎn)換為機(jī)器碼執(zhí)行。
- 優(yōu)點(diǎn):啟動(dòng)速度快,內(nèi)存占用低。
- 缺點(diǎn):執(zhí)行效率低。
- 使用場景:主要用于 Android 5.0 之前的 Dalvik 虛擬機(jī)。
5.2 即時(shí)編譯(JIT,Just-In-Time)
- 工作原理:在程序運(yùn)行時(shí)動(dòng)態(tài)將熱點(diǎn)代碼編譯為本地機(jī)器碼。
- 優(yōu)點(diǎn):動(dòng)態(tài)優(yōu)化,節(jié)省存儲(chǔ)空間。
- 缺點(diǎn):運(yùn)行時(shí)開銷大。
- 使用場景:適用于長時(shí)間運(yùn)行的應(yīng)用。
5.3 預(yù)先編譯(AOT,Ahead-Of-Time)
- 工作原理:在應(yīng)用安裝時(shí),將字節(jié)碼預(yù)先編譯為本地機(jī)器碼。
- 優(yōu)點(diǎn):執(zhí)行效率高,減少運(yùn)行時(shí)開銷。
- 缺點(diǎn):安裝時(shí)間較長,存儲(chǔ)占用高。
- 使用場景:適用于對性能要求高的場景。
5.4 三種方式的比較
| 特性 | 解釋執(zhí)行(Interpretation) | 即時(shí)編譯(JIT) | 預(yù)先編譯(AOT) |
|---|---|---|---|
| 編譯時(shí)機(jī) | 運(yùn)行時(shí)逐條解釋 | 運(yùn)行時(shí)動(dòng)態(tài)編譯熱點(diǎn)代碼 | 安裝時(shí)預(yù)先編譯 |
| 執(zhí)行效率 | 低 | 較高 | 高 |
| 啟動(dòng)速度 | 快 | 較快 | 較慢(安裝時(shí)編譯) |
| 存儲(chǔ)占用 | 低 | 中等 | 高 |
| 運(yùn)行時(shí)開銷 | 無編譯開銷,但解釋開銷高 | 有編譯開銷 | 無編譯開銷 |
| 適用場景 | 低資源設(shè)備、初始階段 | 長時(shí)間運(yùn)行的應(yīng)用 | 高性能要求的應(yīng)用 |
6. 解釋執(zhí)行與 JavaScript 執(zhí)行的比較
解釋執(zhí)行和早期的 JavaScript 執(zhí)行非常相似,都是逐條解釋代碼并動(dòng)態(tài)執(zhí)行。然而,現(xiàn)代 JavaScript 引擎已經(jīng)引入了 JIT 編譯和優(yōu)化技術(shù),執(zhí)行效率大幅提升。以下是它們的相似之處和不同點(diǎn):
6.1 相似之處
- 逐條解釋執(zhí)行:兩者都是逐條讀取代碼并動(dòng)態(tài)執(zhí)行。
- 無需預(yù)先編譯:代碼無需預(yù)先編譯為機(jī)器碼。
- 啟動(dòng)速度快:適合快速加載和執(zhí)行。
- 動(dòng)態(tài)特性支持:支持動(dòng)態(tài)加載和執(zhí)行代碼。
6.2 不同點(diǎn)
- 執(zhí)行效率:現(xiàn)代 JavaScript 引擎通過 JIT 編譯大幅提升了執(zhí)行效率。
- 優(yōu)化能力:現(xiàn)代 JavaScript 引擎具有復(fù)雜的優(yōu)化能力。
- 應(yīng)用場景:解釋執(zhí)行主要用于 Android 虛擬機(jī),而 JavaScript 執(zhí)行主要用于瀏覽器環(huán)境。
7. 總結(jié)
ArtMethod、OatFile::OatMethod 和 ClassLinker 是 ART 虛擬機(jī)中的核心組件,它們在方法管理、代碼執(zhí)行和類加載過程中發(fā)揮著重要作用。通過理解它們的工作原理,我們可以更好地掌握 Android 虛擬機(jī)的運(yùn)行機(jī)制。此外,Android 虛擬機(jī)的三種執(zhí)行方式(解釋執(zhí)行、JIT 和 AOT)各有優(yōu)缺點(diǎn),適用于不同的場景。隨著技術(shù)的發(fā)展,現(xiàn)代 JavaScript 引擎已經(jīng)超越了傳統(tǒng)的解釋執(zhí)行模式,通過 JIT 編譯和優(yōu)化技術(shù)大幅提升了執(zhí)行效率。