JIT (Just-In-Time) — 动态编译技术,将字节码或程序的中间表示形式在执行期间直接转换为机器指令。在 Android 中,JIT 编译器首次出现在 2.2 Froyo 版本的 Dalvik 虚拟机中,将应用执行速度提升了 2-5 倍。根据 Google, 2024,现代 ART 中的 JIT 将解释执行与热点方法分析编译相结合。
要点概览
Just-In-Time (JIT) — 一种编译方法,源代码或字节码不是预先转换(如 AOT),而是在首次调用程序相应部分时转换为机器指令。术语 “Just-In-Time” 意味着编译 “恰好及时” —— 就在执行之前发生。
JIT 的概念自 20 世纪 60 年代就已存在,但随着 1995 年 Java 虚拟机 的出现而得到广泛应用。JIT 结合了字节码的可移植性(一次编写,到处运行)和接近本机代码的性能。在 Java HotSpot VM 中,JIT 编译器分析正在执行的代码,仅编译最关键的部分,从而节省时间和内存。
JIT 编译器 接收字节码作为输入,对其进行解释,同时收集统计信息。当某段代码(方法、循环)被足够频繁地调用时,JIT 决定对其进行编译。编译后的机器码保存在缓存中 —— 后续调用直接使用已准备好的版本。这样既实现了加速,又无需编译整个程序。
// 示例:方法在多次调用后会变为热点
public class HotMethod {
private int compute(int n) {
int sum = 0;
for (int i = 0; i < n; i++) {
sum += i * i;
}
return sum;
}
}
// 在循环中调用 500 次 — JIT 将编译 compute
for (int t = 0; t < 500; t++) {
hot.compute(1000);
}
在 Android 中,JIT 编译 经历了三个演进阶段。第一阶段 —— 没有 JIT 的 Dalvik(Android 1.0-2.1):纯解释执行 DEX 字节码。第二阶段 —— 带有 JIT 的 Dalvik(Android 2.2-4.4):JIT 编译器的出现将应用速度提升了 2-5 倍。第三阶段 —— 带有混合 JIT 的 ART(Android 7.0+):JIT 以新的质量回归。
Dalvik 中的 JIT 实现为 基于跟踪的 编译器。它分析的不是单个方法,而是经常顺序执行的指令链(跟踪)。这使得可以编译包括多个方法在内的整个执行路径。这种方法对于指令缓存较小的 移动处理器 非常有效,因为编译后的跟踪可以容纳在 L1 缓存中。
从 Android 7.0 Nougat 开始,ART 使用 基于方法的 JIT —— 根据执行配置文件编译单个方法。此 JIT 的运行速度比 Dalvik JIT 快得多:单个方法的典型编译时间为 0.5-1 毫秒,而 Dalvik 为 3-5 毫秒。编译后的代码存储在单独的内存区域(JIT 代码缓存)中,而不是应用的堆中,从而减少了碎片化。
| 参数 | Dalvik JIT | ART JIT |
|---|---|---|
| 类型 | 基于跟踪 | 基于方法 |
| 编译速度 | 3-5 毫秒/方法 | 0.5-1 毫秒/方法 |
| 编译阈值 | 约 200 次调用 | 动态 |
| 代码缓存 | 在应用堆中 | JIT 代码缓存 |
| 性能分析 | 内部 | 外部 .prof 文件 |
JIT 的核心机制是 热点方法检测。每次调用方法时,内部计数器都会递增。当计数器超过阈值时,该方法被标记为 “热点” 并发送进行编译。在 Dalvik 中,阈值是硬性规定的(约 200 次调用)。在 ART 中,计数器根据设备的可用资源动态调整。
编译过程包括几个阶段。第一阶段 —— 字节码分析:JIT 研究指令流并构建数据流图。第二阶段 —— 优化:小方法内联、死代码消除、常量折叠。第三阶段 —— 代码生成:将优化后的图转换为特定 CPU 架构(ARM、ARM64、x86)的机器指令。
// 内联演示 — JIT 将替换方法体
public int inlineExample() {
return square(5);
}
private int square(int x) {
return x * x;
} // JIT 将调用替换为 return 5 * 5;
JIT 的一项特殊技术 —— 栈上替换 (OSR)。如果某个方法包含一个长时间运行的循环,数百次迭代仍未完成,JIT 可以 “即时” 编译该循环,并在执行过程中直接用编译后的版本替换解释执行的版本。OSR 对于计算密集型任务尤为有效:渲染、图像处理、加密。
JIT 和 AOT 是两种编译方法,各有不同的取舍。JIT 牺牲首次启动速度以换取分发包的紧凑性和适应性。AOT 牺牲安装时间和磁盘空间以换取从第一秒开始的最佳性能。两种方法都不是绝对最好的 —— 选择取决于具体场景。
JIT 的关键优势在于 自适应优化。JIT 可以使用 AOT 无法获得的性能分析信息:精确的对象类型、实际调用频率、实际分支情况。这使得可以应用静态编译中不可能的激进优化。例如,如果实践中只遇到一种接收者类型,JIT 可以消除虚方法调用(去虚拟化)。
| 指标 | JIT | AOT |
|---|---|---|
| 安装时间 | 即时 | 取决于大小 |
| 首次启动 | 较慢(预热) | 快速 |
| 磁盘空间 | 最小 | +15-30% |
| 适应性 | 高 | 低 |
| CPU 消耗 | 编译时峰值 | 稳定 |
JIT 编译 更适合重视快速部署和节省磁盘空间的场景。在移动开发中,JIT 对于频繁更新的应用(A/B 测试、热修复)是理想选择。同样,JIT 在开发阶段也很方便,当代码一天要重新构建数十次时 —— 每次编译节省的每一秒都会加速反馈循环。
JIT 为开发者提供了一系列实际优势。第一 —— 较小的 APK 体积。采用 JIT 方法时,APK 中只打包字节码(DEX),比编译后的本机代码少占用 20-30% 的空间。对于内置存储有限的用户来说,这是一个显著的优势。
第二个优势 —— 设备适应能力。JIT 根据实际的 CPU 架构、RAM 大小和当前负载编译代码。例如,在 2GB RAM 的设备上,JIT 可以降低编译力度以节省内存,而在 12GB 的旗舰机型上则可以应用所有可能的优化。相比之下,AOT 编译在安装时就已经确定了决策。
字节码 保持平台无关性,简化了应用分发。一个 APK 可以在 ARM、ARM64 和 x86 设备上运行,JIT 则为每种架构生成本机代码。而 AOT 方法则需要在 APK 中包含多种本机代码版本(增加体积),或者为每种架构编译单独的版本。
JIT 的主要缺点 —— 预热延迟(warm-up delay)。用户在应用运行的最初几秒会感到卡顿,因为 JIT 正在编译热点方法。在游戏中,这表现为初始关卡的 “卡顿”(stuttering)。在带有动画的应用中,则是首次界面切换时的抖动。
第二个缺点 —— 能耗。编译过程会密集占用 CPU,在预热期间能耗增加 10-20%。对于电池供电的设备,这会缩短续航时间。在频繁重启应用的场景中尤为明显(内存受限的多任务处理,系统频繁卸载和重新加载进程)。
另一个问题 —— JIT 缓存碎片化。编译后的代码存储在连续的内存区域中。在加载新类和编译额外方法时,缓存会变得碎片化,增加了内存管理的开销。在 Dalvik 中,这个问题通过定期清空缓存来解决;在 ART 中,JIT 缓存从堆中单独分配,并使用自己的碎片整理策略。
现代 ART 采用的方法 —— 混合编译,结合了 JIT 和 AOT 的优势。应用安装时不执行编译 —— 只进行字节码验证。这确保了快速安装和最小的占用空间。首次启动在以解释执行模式运行,并配合 JIT 编译热点方法 —— 用户无需长时间等待即可获得可接受的性能。
同时,一个 后台性能分析器 在运行,收集实际使用数据。经过 2-3 次完整的应用启动后,配置文件达到足够的完整度,系统启动 dex2oat 将热点方法编译为本机代码。此操作在设备空闲时(充电、屏幕关闭)在后台执行。后台 AOT 完成后,应用获得与完整 AOT 编译相当的性能。
# 强制启动后台编译
adb shell cmd package compile -m speed-profile -f com.example.app
# 查看编译状态
adb shell cmd package dump-profiles com.example.app
根据 Google I/O 2017 的数据,混合编译相比纯 AOT 将应用安装时间缩短了 30-50%。系统分区占用空间减少了 20-30%。同时,后台编译后的性能达到了完整 AOT 的水平。混合模式唯一逊于 AOT 的场景是安装后的首次启动:应用以 JIT 模式运行,可能慢 10-15%。
常见问题
JIT 是一种程序的加速方法,代码不是预先转换,而是在运行时逐步转换为机器语言。最常用的部分被编译并缓存,而不常用的部分保持原始形式。
JIT 在执行时编译代码,节省空间并加快安装速度。AOT 预先编译所有代码 —— 应用启动更快,但需要更多的磁盘空间和安装时间。
JIT 没有被移除,而是 演变 了。在 Android 5.0 中,带有 JIT 的 Dalvik 被带有纯 AOT 的 ART 取代。在 Android 7.0 中,JIT 作为混合系统的一部分回归 ART,与后台 AOT 编译协同工作以实现最佳性能。
JIT 由于 CPU 负载,在预热期间能耗增加 10-20%。热点方法编译完成后,能耗恢复到正常水平。ART 的混合模式通过后台编译将这些峰值降至最低。
是的,在计算密集型场景中。用户可能会注意到应用运行最初几秒或游戏开始时的 卡顿。在较新版本的 Android(8.0+)中,混合模式通过基于配置文件的分析编译将此影响降至最低。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。