我们可以直接运行dart源码来执行程序,比如说 那为什么还要搞出个snapshot呢?
snapshot是什么
snapshot简单翻译过来就是:快照,是一系列字节,代表一个或多个Dart对象的序列化形式。这种表示方式与这些Dart对象在孤立的内存堆中的表示方式非常接近。
Dart VM使用快照的主要原因有两个:
- 加快应用程序的初始启动时间。核心库和应用程序脚本的快照通常包含为核心库和应用程序脚本准备的数据。这意味着在启动过程中不必解析和标记化库和脚本。
- 将对象从一个隔离区传递到另一个隔离区
快照可以分以下几类:
- full snapshot: 完整快照,代表隔离堆初始化后的完整表示。Dart VM使用它来快速启动和初始化整个Dart核心库以及其他库,例如dart:convert,dart:io,dart:isolate等。
- script snapshot: 脚本快照,代表了将脚本加载到隔离中之后但开始执行脚本之前隔离堆中应用程序脚本的完整表示。 Dart VM使用它来快速启动和初始化应用程序。例如,启动dart2js的脚本使用dart2js应用程序的预先创建的脚本快照。
- object snapshot: 对象快照。在Dart VM中,通过创建需要发送到另一个隔离对象的Dart对象的快照,来实现从一个隔离对象到另一个隔离对象的消息传递。
如何生成snapshot
通过Dart VM我们可以将.dart转换成.snapshot。下面是最基础公式:
dart [--package_root=] --snapshot=<output_file> <dart_file>
-
对应的是最后<dart_file>编译所依赖的库的地址,一般在当前根目录下的.package文件。 不是很了解的可以看下我上篇文章:Dart的包管理命令解析-pub get的2.2章节。
-
<output_file>对应的是参数是输出文件路径包含文件名。文件一般以.snapshot结尾。源码中常用的有flutter_tools.snapshot、dart.snapshot等。
-
<dart_file> 一般是个dart的main函数。列如上篇文章所说的pub.dart:
import 'package:pub/src/command_runner.dart';
void main(List<String> arguments) {
PubCommandRunner().run(arguments);
}
除了上面几个核心参数还有一个参数。他用来指定snapshot的类型,默认值是kernel 。另外还有一种是app-jit 。
kernel snapshot
kernel快照本身是一种Kernel AST形式的二进制文件,Kernel AST指的是一种Dart VM能够识别的一种二进制中间产物,可以近似的理解为是Java的Class文件。他是架构无关的,因此可以在平台之间迁移使用。
运行的时候执行dart即可,如果有参数可以在快照文件之后加空格跟上。
dart hello.snapshot
app-jit snapshot
尝试打个app-git的试试:
app-aot snapshot
我们发现通过dart -snapshot-kind
编译snapshot只提示有kernel和app-jit两种形式,为啥没有app-aot呢?
因为app-aot在dart2.7之后是通过dart2native
来编译的:
dart2native <dart-file> -k aot
运行方式也不一样,需要使用dartaotruntime环境:
dartaotruntime xxx.aot
但是有个问题是你在flutter源码中找不到dart2native
。
需要我们拉取Dart-lang/dart源码才行。参考dart官网
app-jit和app-aot的区别其实就是
JIT
和AOT
两种编译方式的区别了
native 机器码
运行dart代码基本可以分成三种方式:源码,snapshot,native机器码。前两种粗略的讲过了。
native机器码是在Dart 2.7之后引用的。可以使用dart2native
来讲dart源码编译机器码。
尝试写个test.dart
void main(){
print('this is test log');
}
然后使用dart2native
编译下,编译产物默认使用.exe结尾:
与其他不同的是,运行test.exe不再需要dart环境了。可以直接运行:
参考文献: