原文地址: https://mrale.ph/dartvm/
发现一篇2019年的译文: https://annatarhe.github.io/2019/01/31/introduction-to-dart-vm.html, 部分中文专有名词摘自该译文
备注: 原文仍在处在修改状态, 翻译此文时,原文最后修改时间为2020年1月29日
本文的目标:本文的目标是作为DartVM 开发的参考手册, 供新团队成员、潜在的外部贡献者、或者其他任何对VM内部原理感兴趣的人。 本文从介绍DartVM 简介开始,然后详细介绍了VM中的各个组件
DartVM 由组件构成,他们支撑了原生执行Dart 代码, 主要包括如下部分
- 运行时系统-Runtime System
- 对象模型-Object Model
- 垃圾回收-Garbage Collection
- 快照-Snapshots
- 核心库和原生方法-Core libraries native methods
- 开发体验组件,通过服务协议调用 Development Experience components accessible via service protocol * Debugging * Profiling * Hot-reload
- 调试
- 性能分析
- 热加载
- JIT 和AOT 编译流水线-Just-in-Time (JIT) and Ahead-of-Time (AOT) compilation pipelines
- 代码解释器-Interpreter
- ARM 虚拟机-ARM simulators
Dart 虚拟机是历史继承的。 Dart虚拟机原意是它会提供一个高级编程语言执行环境,但是它不代表Dart 代码在DartVM 上执行时总是解释的或者JIT编译的。 例如,Dart代码可以被DartVM AOT流水线编译成机器码然后在一个叫作预编译的运行时(precompiled runtime)的被裁剪的Dart VM版本上执行,这个预编译的运行时 不包含任何编译器组件并且无法动态加载Dart 源代码。
Dart 虚拟机是怎么运行代码的?
Dart 虚拟机有多种方式来执行代码,例如:
- 使用JIT 从代码执行或者内核二进制
- 从快照执行
- 从AOT 快照
- 从AppJIT 快照
这些不同的方式之间的主要区别是虚拟机何时以及如何把Dart 源代码转换成可执行代码。 支撑运行的运行时环境都是一样的。
独立分区-isolate
任何虚拟机中的Dart代码都是在独立分区(isolate) 中运行,独立分区可以描述为一个独立的Dart宇宙,拥有自己的内存堆并且通常有自己的主控线程(mutator thread)。 很多独立分区的Dart代码可以并发执行,但是这些分区无法直接分享状态,只能通过端口(ports) 不是网络端口,来传递消息来共享。
操作系统线程和独立分区的关系有一些模糊并高度依赖虚拟机是如何继承到应用中的。 只有如下可以得到保障:
- 任何操作系统线程只可以在同一时间进入一个独立分区,要进入另一个独立分区之前必须退出前一个独立分区。
- 一个独立分区在同一时间只能关联一个主控线程(mutator thread)。 主控线程是一个线程能执行dart 代码并使用虚拟机公共的C API接口。
但是同一个操作系统线程可以先进入一个独立分区,执行Dart 代码,然后离开这个独立分区并进入另一个独立分区。另外很多不同的操作系统线程可以进入一个独立分区并在其中执行Dart 代码,不是并发的。
一个独立分区可以关联一个主控线程的同时可以关联多个辅助线程,例如
- 一个后台JIT 编译线程
- 垃圾回收扫描线程
- 并发垃圾回收标记线程
虚拟机内部使用一个线程池(dart::ThreadPool)来管理操作系统线程,代码是基于线程池task 概念构建的而不是操作系统的线程构建的。例如,在触发垃圾回收时,VM 不是新建一个专门的线程来执行后台扫描,而是发送一个dart::ConcurrentSweeperTask 给全局的虚拟机线程池,而线程池的具体实现可以选择一个空闲的线程来执行或者当没有空闲的线程时新建一个线程来执行。类似的独立分区消息处理的事件循环线程也不是一个专门的消息循环线程,而是当新消息来的时候发送一个dart::MessageHandlerTask 给线