Dalvik / GraalVM
过去的选择与未来的方向
这一节是 JVM 虚拟机家族的最后一站:一个是已经谢幕但影响深远的 Android 虚拟机,一个是正在定义未来的新一代运行时。
Dalvik:Android 的第一个虚拟机
出身
2003 年,Google 收购了 Android 公司。Android 团队需要一个能在手机内存和 CPU 约束下高效运行的 Java 虚拟机,但他们没有选择 HotSpot——一是因为许可证问题,二是因为 HotSpot 太重。
于是,他们从零设计了 Dalvik。
关键区别:不是 JVM
Dalvik 是一个 Java 语言的实现,但不是一个 JVM 实现。它运行 Dex 字节码,而 Dex 不是 Class 文件格式。
Java 源码 (.java)
↓ javac
Class 文件 (.class) Dex 文件 (.dex)
↓ dx 工具 ← Android SDK 工具链
JVM 执行 Dalvik / ART 执行Android SDK 提供了一个 dx 工具,把所有的 Class 文件打包成一个 Dex 文件:
# 编译 → 转换 → 运行
javac *.java # 编译成 Class 文件
dx --dex --output=classes.dex *.class # 转换成一个 Dex 文件
dalvik -cp classes.dex MainActivity # Dalvik 执行 DexDalvik 的设计优化
Dalvik 针对手机做了大量定制:
1. 寄存器式虚拟机
Dalvik 没有使用 JVM 的基于栈的设计,而是选择了基于寄存器的指令集。
// JVM 字节码(基于栈):
iload_1 // 局部变量槽 1 入栈
iload_2 // 局部变量槽 2 入栈
iadd // 栈顶两数相加
// Dalvik 字节码(基于寄存器):
add-int v0, v1, v2 // v1 + v2 → v0寄存器式指令通常比栈式指令执行效率更高(省去了入栈/出栈),但代价是跨平台适配更复杂。Dalvik 只需要支持 ARM 架构,不需要考虑跨平台。
2. 一个 Dex,多个 Class
JVM 中每个 Class 有一个独立的 Class 文件。Android 应用可能有几十个 Class,JVM 需要分别加载。
Dalvik 把所有 Class 打包成一个 Dex 文件。加载时只需要一次 I/O,节省了 SD 卡读取时间——这对当时的手机来说很重要。
3. 共享的 Dex
多个应用可以共享同一个 Dex(通过 sharedUserId 机制),减少手机的存储占用。
Dalvik 的生命周期
2008 Android 1.0 ──→ Dalvik 虚拟机首次登场
2010 Android 2.2 ──→ 引入 JIT 编译器(Dalvik JIT)
2014 Android 5.0 ──→ Dalvik 被 ART 取代
2017 Android 8.0 ──→ 完全移除 DalvikDalvik 在 Android 存在了 9 年(2008~2017),服务了数十亿台设备。
ART:Dalvik 的继承者
什么是 ART
ART(Android Runtime)是 Android 5.0 引入的新运行时,取代了 Dalvik。
ART 的核心改变是:AOT(Ahead-Of-Time)编译。
Dalvik:每次运行应用,都要把 Dex 字节码解释/即时编译
→ 启动慢,每次都编译,耗电
ART:安装应用时,就把 Dex 编译成本地机器码
→ 启动快,只编译一次,省电ART 牺牲了安装速度(安装时需要编译),换来了运行时的流畅度和续航。
混合模式(JDK 7+)
2017 年 Android 8.0 引入了 ART 的混合编译模式:
- 安装时做部分 AOT 编译(加快安装速度)
- 运行中用 JIT 补充编译热点代码
- 最终把 JIT 编译结果缓存下来
这其实就是 HotSpot 分层编译思想的 Android 版本。
GraalVM:JVM 的未来形态
为什么需要 GraalVM
HotSpot 是一个用 C++ 写的虚拟机。C++ 的问题是:
- 维护成本高:C++ 代码需要手动内存管理,容易出现 bug
- 优化难度大:编译器层面的优化受限于 C++ 的表达能力
- 多语言支持受限:不同语言想要在 HotSpot 上运行得好,需要自己实现 Truffle 框架
GraalVM 的答案是:用 Java 实现 JVM。TL;DR:如果 JVM 本身是用 Java 写的,那么优化它、扩展它、让它支持多语言,都会容易得多。
GraalVM 的架构
┌─────────────────────────────────────────────────────────────┐
│ GraalVM │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ GraalVM Languages (Truffle) │ │
│ │ JavaScript Python Ruby R LLVM WASM ... │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ GraalVM Core │ │
│ │ Graal Compiler (用 Java 实现的 JIT 编译器) │ │
│ │ Substrate VM(用于 AOT 编译) │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ JVMCI + HotSpot / SVM │ │
│ │ 作为 HotSpot 的 JIT 插件 / 独立 VM 运行 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘GraalVM 有两种使用模式:
模式一:HotSpot 的 JIT 插件
HotSpot JVM
↓ JVMCI 接口(Java-Level JVM Compiler Interface)
Graal JIT 编译器(用 Java 实现)
↓
更高效的机器码Graal 作为 HotSpot 的 JIT 插件,替代 C2 编译器。Graal 的优化能力比 C2 更强,特别是在特定场景下。
模式二:独立虚拟机(SubstrateVM)
GraalVM Native Image(SubstrateVM)
↓ AOT 编译
原生可执行文件(不含 JVM)
↓
直接运行,不需要 JVM 安装SubstrateVM 可以在构建时把 Java 应用和 GraalVM 一起编译成一个独立的原生可执行文件。没有 JVM 启动开销,启动速度从「秒级」变成「毫秒级」,内存占用也大幅降低。
# 安装 GraalVM 后
gu install native-image
# 编译成原生可执行文件
native-image -jar myapp.jar myapp
# 运行
./myapp
# 启动时间从 2 秒变成 20 毫秒GraalVM 的多语言支持:Truffle 框架
GraalVM 的另一个杀手锏是Truffle 框架——让任何语言都可以用 GraalVM 作为运行时。
传统方式:每种语言 → 自己实现解释器 → 性能差
Truffle 方式:语言 → Truffle AST 解释器 → Graal 自动优化 → 高性能Truffle 让语言实现者只需要专注于语言本身的语义,不需要操心 JIT 优化。Graal 会自动把「解释执行」的 AST 编译成高效的机器码。
目前支持的语言:
| 语言 | 说明 |
|---|---|
| JavaScript | 速度比 V8 稍慢,但差距不大 |
| Python | GraalPython,比 CPython 快 |
| Ruby | TruffleRuby,比 CRuby 快 |
| R | FastR,适合数据处理 |
| LLVM | 可以运行 C/C++ 编译出的 bitcode |
GraalVM 的适用场景
GraalVM 不是银弹,它最适合以下场景:
| 场景 | 原因 |
|---|---|
| 微服务 / Serverless | 原生镜像启动极快,适合无服务器架构 |
| 多语言混合 | 一个进程里跑 Java + JS + Python,无互操作开销 |
| JVM 性能优化 | 用 Graal JIT 替代 C2,提升特定场景性能 |
| 工具类应用 | 命令行工具需要秒级启动,原生镜像完美契合 |
| 数据库扩展 | PostgreSQL 的 PL/Java 用 GraalVM 加速 |
GraalVM 与 JDK 的关系
从 JDK 9+ 开始,Graal 已经被纳入 JDK 的模块系统:
# JDK 17 内置 Graal
java -XX:+UseGraalJIT MyApp
# 或者用 GraalVM 发行版(JDK 17+)
# https://www.graalvm.org/Oracle GraalVM 是最完整的发行版,包含了所有特性。OpenJDK 用户也可以通过 JAVA_HOME 切换使用 Graal JIT。
Dalvik 与 GraalVM 的对比
| 维度 | Dalvik | GraalVM |
|---|---|---|
| 目标 | 移动设备,低资源 | 所有场景,高性能 |
| 实现方式 | 从零实现 | 用 Java 实现 JVM |
| 字节码 | Dex(非标准) | 标准 Class 字节码 |
| 核心创新 | 寄存器式,Dex 打包 | Truffle 框架,原生镜像 |
| 生命周期 | 2008~2017,已停止 | 2013~至今,活跃发展中 |
| 影响 | 证明了非 JVM 的 Java 实现可行 | 正在重新定义 JVM 的未来 |
本节小结
Dalvik 和 GraalVM 代表了两个极端:
- Dalvik:从零实现,专为移动端优化,最终被 Google 放弃——它证明了 JVM 的替代品可以存在,但也证明了最终还是要拥抱标准
- GraalVM:用 Java 实现 JVM,让 JVM 本身可以被优化和扩展——它代表了「元编程」的理念:如果你想让系统变得更好,就用这个系统本身的语言来实现它
到这里,「主流 JVM 虚拟机介绍」的全部章节就结束了。我们从 SUN Classic 一路讲到 GraalVM,JVM 生态的过去和未来都覆盖到了。
如果你想继续深入 JVM 之旅,接下来可以进入 JVM 运行时数据区(核心) 部分,这是 JVM 最核心、最实用的知识点。
