探索Flutter学习之旅:Dart虚拟机(VM)详解

发表时间: 2021-08-27 00:00

Dart 是在虚拟机Dart VM上运行的。Dart VM是由以下组件构成:

  • Runtime 系统: Object Model、GC(垃圾回收)、Snapshots(快照)
  • 解释器
  • JIT(Just-in-Time)以及AOT(Ahead-of-Time)编译管道
  • 内核库
  • ARM模拟器

Dart VM既可以像V8引擎一样实时编译JavaScript代码运行,又可以通过AOT管道运行编译后的二进制工程(AOT模式下不支持热加载)。

VM如何运行代码

Dart最终是基于机器语言执行的,但是根据何时以及如何编译成机器语言,VM分为三种运行模式:

  • JIT模式
  • AOT 快照模式
  • APPJIT快照模式

Dart 代码运行在Isolate中,一个Isolate包括独立的堆(heap)以及控制线程Mutator Thread:运行Dart代码以及多个辅助县城Helper Thread:GC(内存管理)、JIT(代码解析、解释、编译)等等构成。通常Flutter运行在一个Isolate中,本质是单线程运行的,如果有需要,可以创建其他线程(Isolate)运行耗时的程序。Isolate可以并发执行(也就是多线程),Isolate之间可以通过消息机制通信,不能共享状态。

  • Dart VM中线程是通过线程池管理的
  • 同一时间一个线程只能归属于一个Isolate
  • 一个Isolate中只包含一个Mutator Thread用于执行 Dart Code


runtime

如上图所示,左边以及右边表示一个普通Isolate,包含Heap(储存对象以及状态),Mutator Thread以及Helper Thread。中间是一个VM Isolate,包含由系统创建的不可改变的对象(null、true、false),其他Isolate可以引用这个Isolate中的对象,但是不可以更改它。

kernel service

Dart Code首先通过Kernel Service中的CFE(common front-end)将Dart 代码转化为Kernel 二进制文件(本质是二进制的AST抽象语法树),这是一个中间文件,不是机器可执行的二进制文件。

生成的中间文件也叫做dill文件,它可以通过不同的dart工具解析处理,用于不同目的:Dart VM的isolate可以执行dill文件,dart2js工具可以把它编译为js文件,打包成web工程。

cfe

将dart代码转化为dill文件(kernel二进制文件)和执行kernel二进制文件这两个过程,可以分开由来两个不同机器来完成,比如Flutter的debug模式,可以有PC端通过CFE转换,通过adb将数据发送给移动设备,移动端Dart VM执行kernel文件。

APPJIT 快照模式

引入AppJIT快照可减少大型Dart应用程序(如dartanalyzer或dart2js)的JIT预热时间。当这些工具用于小型项目时,它们花费的实际时间与VM花费的JIT编译这些应用程序的时间一样多。

AppJIT快照可以解决此问题:可以使用一些模拟训练数据在VM上运行应用程序,然后将所有生成的代码和VM内部数据结构序列化为AppJIT快照。然后可以分发此快照,而不是以源(或内核二进制)形式分发应用程序。如果出现实际数据上的执行配置文件与培训期间观察到的执行配置文件不匹配,快照开始的VM仍可以采用JIT模式执行。

AOT快照模式

AOT

Dart Source经过CFE生成kernel二进制文件,然后经过TFA(类型流分析)进行全局静态分析,确定变量以及领域类型,以及确定从已知入口那些部分可以访问到,分配了哪些类的实例以及类型如何在程序中流动。所有可能被访问的代码都将被编译为本地代码。编译完所有函数之后,就获得了AOT快照,快照可以直接在Runtime中运行。

AOT snapshot