Google 的 Dart 团队推出了新一代代码分析工具,并为之取名“天文台” Observatory,让我这个大气物理专业的学生顿生亲切感。迫不及待地试乘试驾过后,发现确实名副其实。
- 得到代码执行的各个函数所耗费时间。
- 检查内存和CPU的资源分配。
- 检查代码行是否被执行--Code Coverage
- 调试内存泄漏。
- 调试内存碎片。
豪车的配置和我们的座驾也都差不多,但带来的尊贵体验的享受却是天上地下,所有的这些功能都是图形化的,可点击进一步跟踪代码详情,还可以通过命令行 动态输入参数和代码 来调试输出结果。
因为都是图形化的,用颜色块来区分成功、失败或问题,轻点鼠标就能不断深入地探索宇宙的奥秘。这种感觉,确实就像抬头仰望星空。
一、开始使用
aqueduct serve
和 bin/main.dart
两种方式都支持启动 Observatory。 当使用aqueduct serve
运行应用程序时,添加--observe
标志,
aqueduct serve --observe
Observatory将开始在端口8181上进行监听,并且Web浏览器将自动打开。我们先看看Code Coverage
二、 Observatory常用术语
1.垃圾回收(GC)
垃圾收集Garbage collection是搜索堆以查找和回收应用程序不再使用的“dead”内存区域的过程。 此过程允许重新使用内存,并最大限度地降低应用程序内存不足的风险,避免导致内存崩溃。
垃圾收集由Dart VM自动执行。 在Observatory中,您可以通过点击 Allocation Profile 屏幕中的GC 按钮按需执行垃圾收集。
2.堆栈Heap
Dart对象被动态地分配在内存的一部分中,称为堆栈heap。 当没有指向它的时候,或者当应用程序终止时,从堆栈中分配的对象将被释放(符合垃圾回收的条件)。 当没有指向一个对象时,它被认为是死的dead。 当一个对象被另一个对象指向时,它是活的live。 查看dart 参考页面.
3.隔离Isolates
Dart支持通过隔离isolates的方式执行并发执行,您可以将其视为进程而无需开销。 每个隔离区都有其自己的内存和代码,不受其他隔离区的影响。 有关更多信息,请参阅 The Event Loop and Dart。
每个Dart应用程序至少包含一个名为root的隔离区。 当您启动Observatory时, VM screen会列出应用程序的所有隔离区。 您可以通过单击该隔离区的名称,单独浏览每个隔离区并与其进行交互。
4.内存碎片Memory fragmentation
内存碎片Memory fragmentation 发生在空闲内存被分割成小块散布在整个分配内存中时。 这种现象会对应用程序的性能产生负面影响,并可能导致内存不足异常。
你可以使用Observatory的堆映射Heap map找到内存碎片。 此功能显示用以白色色块表示空闲内存。 如果有许多小白色区域遍布在有色区域中,则表明应用程序的内存碎片过多需要优化。 有关更多信息,请参阅Heap Map。
5.内存泄漏Memory leak
内存泄漏memory leak 通常发生在当一个对象处于活动状态时(意味着另一个对象指向它)但它没有被使用(所以它不应该有其他对象的引用)时。 这样的对象不能被垃圾回收,所以它占用堆中的空间并且造成内存碎片memory fragmentation。 内存泄漏给虚拟机VM 带来了不必要的压力,并且很难调试。
6.新一代New generation
新一代New generation 是指大多数新创建的对象(除非它们非常大)分配在堆的一部分中。 新一代特别适合临时和短暂的对象 - 它很小,设计得很快就能收集垃圾。
7.老一代Old generation
当一个对象已经存在了一段时间并且在垃圾收集周期中存活下来时,它通常被提升为堆的一部分,我们称之为老一代old generation, 它为新创建的对象释放新一代。
Observatory 的heap map 功能为您提供了一种可视化浏览老一代old generation的方式。 有关更多信息,请参阅Heap Map。
8.虚拟机Virtual machine (VM)
Dart虚拟机是一个可以直接执行Dart代码的软件。 Dartium浏览器是包含Dart VM的特殊版本的Chromium,可以运行Dart代码,而无需预先将其编译为JavaScript。
当您将Dart web app 编译为JavaScript时,您可以在任何现代浏览器中运行它。
三、测试用的本地数据库
Dart通常使用PostgreSQL 数据库。
运行自动化测试的应用程序默认使用以下配置连接到数据库:
username: dart
password: dart
host: localhost
port: 5432
databaseName: dart_test
在本地安装PostgreSQL之后,您可以通过运行以下命令来创建与此连接信息匹配的数据库用户和数据库:
aqueduct setup
Aqueduct测试会创建一个临时数据库schema,该模式与dart_test数据库中的应用程序schema相匹配。 测试完成后,该数据库中的表格和数据将被销毁。 出于这个原因,在这个数据库中不应该创建其他表以避免与测试冲突。 Aqueduct测试的这种默认行为由test harness提供。
四、运行应用程序的本地数据库
独立于测试数据库的本地数据库,用于本地运行应用程序。 您可以通过运行psql来打开PostgreSQL终端并运行以下命令来在本地创建数据库:
CREATE DATABASE my_local_app_db;
CREATE USER my_local_app_user WITH PASSWORD 'mypassword';
GRANT ALL ON DATABASE my_local_app_db TO my_local_app_user;
通过生成并执行迁移脚本将您的schema添加到本地数据库:
aqueduct db generate
aqueduct db upgrade --connect postgres://my_local_app_user:mypassword@localhost:5432/my_local_app_db
五、使用本地配置文件
使用配置文件 configuration files 来管理应用程序连接到哪个数据库。 根据开发团队的偏好,这可以加入到源代码管理中。 控制哪个文件使用命令行选项加载到aqueduct serve
或bin/main.dart
脚本中:
aqueduct serve -c local.yaml
六、根据情景分配脚本
为了测试客户端应用程序,您通常会希望在本地数据库中拥有一组特定的数据。 创建bin
脚本以配置数据库并添加所需的数据。 例如,您可能有一个名为bin/ios_integration.dart
的脚本,它重新设置数据库并使用您的应用程序中声明的 Query<T> 实例和ManagedObject<T>向其中插入数据。
import 'dart:io';
import 'package:myapp/myapp.dart';
Future main() async {
await provisionDatabase();
var defaultUser = new User(...);
var query = new Query<User>()..values = defaultUser;
await query.insert();
...
}
Future provisionDatabase() async {
var commands = [
"CREATE DATABASE local_app;",
"CREATE USER local_user WITH PASSWORD 'local';",
"GRANT ALL ON DATABASE local_app TO local_user;"
];
await Future.forEach(commands, (cmd) {
List<String> args = ["-c", cmd, "-U", grantingUser];
return Process.run("psql", args, runInShell: true);
});
}