Dart VM(虚拟机)是 Dart 语言的运行时环境,负责执行 Dart 代码。它不仅支持开发时的快速迭代(如热重载),还能在移动端(Flutter)、服务器端(如 Dart 的独立应用)等场景下高效运行代码。以下是 Dart VM 运行代码的核心机制和流程:
1. 代码加载与解析
- 源代码加载
Dart VM 从文件系统或内存中读取 Dart 源代码(
.dart
文件),或直接接收字节码(如 AOT 编译后的产物)。 - 词法与语法分析
将源代码解析为抽象语法树(AST),检查语法错误(如缺少分号、类型不匹配等)。此阶段在开发时(如
dart run
)会实时进行。
2. 编译阶段
Dart VM 支持两种编译模式,具体行为取决于运行方式:
A. JIT(即时编译)模式
- 动态编译
在开发阶段(如 Flutter 的热重载),Dart VM 使用 JIT 将 Dart 代码动态编译为机器码(Native Code),无需提前编译。
- 优化过程
- 热点代码优化:VM 会监控运行中的代码,对频繁执行的函数进行内联、循环展开等优化。
- 类型反馈:通过运行时类型信息(RTI)动态优化类型相关的操作(如将动态调用转为静态调用)。
- 特点
- 启动快,适合调试和快速迭代。
- 运行时性能略低于 AOT,但可通过优化逐步提升。
B. AOT(Ahead-of-Time)编译模式
- 预编译为机器码
在发布应用时(如 Flutter 的
flutter build apk
),Dart 代码会被提前编译为机器码(通过 dart compile aot
或 Flutter 工具链)。 - 优化手段
- 静态分析:在编译时确定类型、消除冗余检查。
- Tree Shaking:移除未使用的代码,减少二进制体积。
- 特点
- 启动慢(需加载预编译的二进制文件),但运行时性能接近原生代码。
- 无 JIT 开销,适合生产环境。
3. 运行时执行
无论 JIT 还是 AOT,最终代码都会以机器码形式执行。Dart VM 的运行时包含以下关键组件:
A. 内存管理
- 垃圾回收(GC)
Dart VM 使用分代垃圾回收器(Generational GC),分为新生代(Young Generation)和老生代(Old Generation):
- 新生代:使用 Scavenge 算法快速回收短生命周期对象。
- 老生代:使用 Mark-Sweep 或 Mark-Compact 算法回收长生命周期对象。
- 内存隔离
每个 Isolate(Dart 的并发单元)拥有独立的内存空间,避免共享状态导致的竞态条件。
B. 原生交互
- FFI(Foreign Function Interface)
允许 Dart 直接调用 C/C++ 库(如操作系统 API 或高性能计算库)。
- 平台通道(Platform Channels)
在 Flutter 中,Dart 通过平台通道与原生代码(Android/iOS)通信。
C. 异步支持
- 事件循环(Event Loop)
Dart 的异步操作(如
Future
、async/await
)由事件循环驱动,单线程模型下高效处理 I/O 密集型任务。 - Isolate 并发
多线程场景下,通过 Isolate 实现真正的并行计算(每个 Isolate 有独立的事件循环和内存)。
4. 调试与优化工具
- Dart DevTools
提供性能分析、内存监控、断点调试等功能,帮助开发者优化 JIT 模式下的代码。
- AOT 的优化报告
使用
dart compile aot --dump-info
生成编译报告,分析优化效果。
5. 关键区别:JIT vs AOT
6. 示例场景
开发阶段(JIT)
dart run bin/main.dart # 动态编译并运行
生产发布(AOT)
flutter build apk # 生成 AOT 编译的 Android 应用