AOT — 是什么,提前编译以及如何工作

作者: IT Sectr 发布日期: 2026-04-16 阅读时间: 9 分钟

AOT(Ahead-Of-Time) — 一种编译技术,将源代码或字节码在程序运行之前(构建或安装阶段)转换为机器指令。在Android中,AOT编译成为ART运行时环境的关键创新,它在5.0 Lollipop版本中取代了Dalvik。根据Google, 2024的数据,ART中的AOT编译消除了预热延迟,并将应用程序的能耗相比JIT方法降低了10–15%。

要点

  • AOT — 提前编译:在程序运行前将代码转换为机器码。
  • 在Android中,AOT由dex2oat工具在APK安装时或后台执行。
  • AOT的主要优势 — 应用程序即时启动,无需预热阶段。
  • 缺点 — 安装时间增加,磁盘额外占用空间增加15–30%。
  • 现代系统使用混合方法:首次启动用JIT,热点方法用AOT。

什么是AOT编译?

Ahead-Of-Time(AOT) — 一种编译方法,程序在运行之前被转换为机器码。术语“Ahead-Of-Time”与JIT(Just-In-Time)相对:JIT是“及时”编译,而AOT是“预先”编译。AOT编译器接收源代码或中间表示(字节码),并生成可执行文件,准备运行。

AOT的历史可以追溯到传统的C和C++编译器,其中编译总是在运行之前进行。在托管语言(Java、C#、Dart)的背景下,AOT是较新的创新:长期以来,人们认为动态特性(反射、动态类加载)使AOT难以实现。Google为Android解决了这个问题,创建了dex2oat —— 将DEX字节码编译为本地代码的AOT编译器。

AOT的工作原理

AOT编译器执行完整的翻译周期。第一阶段 — 解析和构建抽象语法树(AST)。第二阶段 — 分析和优化:死代码消除、内联、循环优化。第三阶段 — 为目标架构(ARM、ARM64、x86)生成机器码。结果是一个可执行文件,在运行时不需要额外处理。

bash
# 手动运行AOT编译器dex2oat
dex2oat --dex-file=classes.dex \
        --oat-file=classes.oat \
        --arch=arm64 \
        --instruction-set-variant=generic

# 检查已编译的OAT文件
oatdump --oat-file=classes.oat --output=oat_dump.txt

Android中的AOT:dex2oat和OAT文件

在Android中,AOT编译通过dex2oat工具(dalvik executable to optimized android translator)实现。当用户安装应用程序时,系统启动dex2oat,读取APK中的DEX文件,优化字节码并创建OAT文件 — 带有本地代码的ELF二进制文件。该文件存储在/data/dalvik-cache/分区中。

编译过程包括多个优化级别。基础级别 — 字节码验证和基本优化(死代码消除、常量折叠)。中级 — 方法内联、循环展开、逃逸分析。最高级 — 整个应用程序的全局优化,包括去虚拟化和栈大小优化。优化级别取决于编译模式(speed、speed-profile、space)。

OAT文件的结构

OAT文件具有ELF(Executable and Linkable Format)格式 — 与本地Linux二进制文件使用的格式相同。在OAT文件内部,包含应用程序每个方法的编译代码以及元数据:关于类、字段、方法及其之间关系的信息。ART使用这些元数据来快速加载类和解析符号引用,而无需完整解析DEX。

OAT组件用途
ELF headerELF格式头
Code section已编译方法的机器码
OAT headerART元数据:版本、节区大小
DEX sections用于反射的原始DEX数据
Link table用于JNI和本地库的连接表

AOT vs JIT:对比分析

AOT和JIT代表了在性能和灵活性之间的不同权衡点。AOT从第一秒起就提供最大的执行速度,但需要更多的磁盘空间和安装时间。JIT节省空间和安装时间,但以预热延迟和峰值能耗为代价。

选择的关键因素 — 使用场景。对于一次性启动并长时间运行的应用程序(游戏、编辑器、导航器),AOT是首选 — 编译成本通过稳定的性能得到回报。对于很少启动、运行时间短的小工具,JIT可能更有利 — 快速安装和占用空间小比峰值性能更重要。

标准AOTJIT
启动即时需预热
安装更长(编译)快速
磁盘空间+15–30%最小
能耗稳定编译时峰值
适应性

代码性能

一个有趣的细微差别:AOT代码并不总是比JIT快。JIT可以访问运行时分析信息 — 对象的精确类型、调用频率、实际分支模式。这使得能够应用AOT无法实现的优化(例如,基于分析的内联)。在实践中,AOT和JIT之间编译代码的性能差异取决于场景,约为±5–10%。

AOT编译的优势

AOT为移动应用程序提供了三个关键优势。第一 — 可预测的性能。用户在前几秒内不会看到“卡顿”:应用程序从第一帧就以最大速度运行。这对于游戏、动画和具有流畅过渡的界面至关重要。

第二 — 能效。AOT不会产生JIT编译特有的CPU峰值负载。处理器以稳定模式运行,在应用程序运行的前30–60秒内将能耗降低10–15%。对于每天启动20–30个应用程序的典型用户来说,这显著延长了电池续航时间。

简化运行时

AOT编译简化了运行时环境。当所有代码已经编译完成后,不再需要在运行时配备JIT编译器、解释器和分析器。这减小了运行时环境本身的大小,并降低了错误概率。完整AOT模式下的ART比具有活动JIT的类似环境少使用约15%的内存。

AOT编译的缺点

AOT的主要缺点 — 安装时间。在早期的Android 5.0设备上,由于AOT编译,大型应用程序(100–200 MB)的安装可能需要2–5分钟。这造成了负面用户体验:下载APK后,必须等待才能打开应用程序。Google在Android 7.0中通过切换到混合方案部分解决了这个问题。

第二个缺点 — 占用空间。OAT文件比原始DEX文件大15–30%。在具有8–16 GB内置存储的设备上,每个应用程序都会在系统分区“吃掉”额外的空间。对于安装了大量应用程序(50–100个)的用户,这可能导致系统更新空间不足。

缺乏适应性

AOT代码在编译时被固定。如果应用程序根据Android版本、设备型号或用户设置使用不同的执行模式,AOT无法适应。为一个场景选择的优化可能对另一个场景不是最优的。JIT在这方面更加灵活:当执行条件改变时,它会重新编译热点方法。

Android之外的AOT:Flutter、.NET、Go

AOT编译不仅用于Android。Flutter使用AOT将Dart代码编译为iOS和Android的本地代码。这确保了即使在弱设备上也能实现60 fps的UI性能。在开发阶段,Flutter使用JIT(热重载),而对于发布版本则使用AOT,结合了两种方法的优势。

.NET生态系统中,ReadyToRun(R2R)技术允许将程序集预先编译为本地代码。这将.NET应用程序的启动时间缩短了30–50%。Go编译器本质上是AOT编译器:Go程序被编译成一个没有外部依赖的静态二进制文件,使其成为容器环境的理想选择。

dart
// Flutter:将Dart代码AOT编译为本地代码
// 发布版本使用AOT
flutter build apk --release

// 结果:带有AOT编译的Dart代码的libapp.so
// 开发使用JIT(热重载)
flutter run

AOT与安全性

AOT的另一个优势 — 增加逆向工程难度。编译后的本地代码比字节码更难反编译。JADX和APKTool等工具可以处理DEX格式,但无法从OAT文件以相同的详细程度恢复源代码。这不能替代混淆(ProGuard、R8),但为分析者创造了额外的障碍。

混合策略:基于分析的编译

Android中的现代标准 — 基于分析的AOT编译,从Android 7.0开始在ART中实现。安装时,应用程序不会完全编译 — 而是使用快速字节码验证和JIT进行首次启动。这解决了Android 5.0–6.0中纯AOT特有的安装时间过长问题。

在应用程序启动2–3次后,ART分析器收集实际使用数据,确定哪些方法对性能最关键。然后在后台(通常在夜间设备充电时),dex2oat将这些热点方法编译为本地代码。后台编译后,应用程序获得与完整AOT相当的性能,而不会对安装时的用户体验产生负面影响。

kotlin
// 编程控制编译模式(Android 9+)
fun requestProfileCompilation(context: Context) {
    val pm = context.packageManager
    // 建议使用基于分析的编译
    pm.setComponentEnabledSetting(
        ComponentName(context, javaClass()),
        PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
        PackageManager.DONT_KILL_APP
    )
}

混合模式的优化

为了从混合编译中获得最大收益,开发人员应遵循几条规则。使用基准分析文件(baseline profiles) — 预先收集的分析文件,随APK一起提供,使ART能够在安装后立即开始热点方法的AOT编译。Baseline profiles将完全性能达到时间从2–3次启动缩短到首次启动。

常见问题

简单来说,什么是AOT编译?

AOT — 是在用户运行之前将程序提前翻译成机器码。想象一下,一本书在您打开之前已经完全翻译成中文了 — 您可以直接阅读,无需等待翻译页面。

AOT与JIT有什么区别?

AOT在安装时编译代码(安装时间更长,但启动更快)。JIT在运行时编译代码(安装快速,但前几秒钟应用程序较慢)。现代系统结合了两种方法。

为什么Android从Dalvik切换到带有AOT的ART?

Google希望消除JIT预热问题 — 应用程序运行前几秒的延迟。ART中的AOT编译确保了即时启动并降低了能耗,这对移动设备至关重要。

AOT如何影响应用程序的大小?

APK大小不变 — AOT编译在系统分区上创建oAT文件,这些文件比原始DEX大15–30%。用户看到的是内置存储可用空间减少,而不是下载文件大小的增加。

什么是基于分析的AOT?

这是一种混合方法,应用程序的首次启动使用JIT,然后系统在后台仅将常用方法编译为本地代码。这结合了JIT的快速安装和AOT的高性能。

总结

  • AOT(Ahead-Of-Time) — 在程序运行之前(安装阶段)将字节码编译为机器码。
  • 在Android中,AOT通过dex2oat工具实现,创建ELF二进制文件(OAT文件)。
  • AOT的主要优势:即时启动、稳定的性能和低能耗。
  • 主要缺点:安装时间增加,额外磁盘空间占用15–30%。
  • AOT不仅用于Android,还用于Flutter(Dart)、.NET(R2R)和Go。
  • 现代ART使用基于分析的AOT:首次启动用JIT,后台编译热点方法。
  • Baseline profiles允许在应用程序安装后立即开始关键方法的AOT编译。

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读