Google 的 Dart 團(tuán)隊推出了新一代代碼分析工具卷胯,并為之取名“天文臺” Observatory陨囊,讓我這個大氣物理專業(yè)的學(xué)生頓生親切感苍蔬。迫不及待地試乘試駕過后麦牺,發(fā)現(xiàn)確實名副其實惰蜜。
- 得到代碼執(zhí)行的各個函數(shù)所耗費時間昂拂。
- 檢查內(nèi)存和CPU的資源分配。
- 檢查代碼行是否被執(zhí)行--Code Coverage
- 調(diào)試內(nèi)存泄漏抛猖。
- 調(diào)試內(nèi)存碎片格侯。
豪車的配置和我們的座駕也都差不多,但帶來的尊貴體驗的享受卻是天上地下财著,所有的這些功能都是圖形化的联四,可點擊進(jìn)一步跟蹤代碼詳情,還可以通過命令行 動態(tài)輸入?yún)?shù)和代碼 來調(diào)試輸出結(jié)果撑教。
因為都是圖形化的朝墩,用顏色塊來區(qū)分成功、失敗或問題伟姐,輕點鼠標(biāo)就能不斷深入地探索宇宙的奧秘收苏。這種感覺,確實就像抬頭仰望星空愤兵。
一鹿霸、開始使用
aqueduct serve
和 bin/main.dart
兩種方式都支持啟動 Observatory。 當(dāng)使用aqueduct serve
運行應(yīng)用程序時秆乳,添加--observe
標(biāo)志懦鼠,
aqueduct serve --observe
Observatory將開始在端口8181上進(jìn)行監(jiān)聽,并且Web瀏覽器將自動打開屹堰。我們先看看Code Coverage
二肛冶、 Observatory常用術(shù)語
1.垃圾回收(GC)
垃圾收集Garbage collection是搜索堆以查找和回收應(yīng)用程序不再使用的“dead”內(nèi)存區(qū)域的過程。 此過程允許重新使用內(nèi)存双藕,并最大限度地降低應(yīng)用程序內(nèi)存不足的風(fēng)險淑趾,避免導(dǎo)致內(nèi)存崩潰。
垃圾收集由Dart VM自動執(zhí)行忧陪。 在Observatory中扣泊,您可以通過點擊 Allocation Profile 屏幕中的GC 按鈕按需執(zhí)行垃圾收集。
2.堆棧Heap
Dart對象被動態(tài)地分配在內(nèi)存的一部分中嘶摊,稱為堆棧heap延蟹。 當(dāng)沒有指向它的時候,或者當(dāng)應(yīng)用程序終止時叶堆,從堆棧中分配的對象將被釋放(符合垃圾回收的條件)阱飘。 當(dāng)沒有指向一個對象時,它被認(rèn)為是死的dead。 當(dāng)一個對象被另一個對象指向時沥匈,它是活的live蔗喂。 查看dart 參考頁面.
3.隔離Isolates
Dart支持通過隔離isolates的方式執(zhí)行并發(fā)執(zhí)行,您可以將其視為進(jìn)程而無需開銷高帖。 每個隔離區(qū)都有其自己的內(nèi)存和代碼缰儿,不受其他隔離區(qū)的影響。 有關(guān)更多信息散址,請參閱 The Event Loop and Dart乖阵。
每個Dart應(yīng)用程序至少包含一個名為root的隔離區(qū)。 當(dāng)您啟動Observatory時预麸, VM screen會列出應(yīng)用程序的所有隔離區(qū)瞪浸。 您可以通過單擊該隔離區(qū)的名稱,單獨瀏覽每個隔離區(qū)并與其進(jìn)行交互吏祸。
4.內(nèi)存碎片Memory fragmentation
內(nèi)存碎片Memory fragmentation 發(fā)生在空閑內(nèi)存被分割成小塊散布在整個分配內(nèi)存中時对蒲。 這種現(xiàn)象會對應(yīng)用程序的性能產(chǎn)生負(fù)面影響,并可能導(dǎo)致內(nèi)存不足異常犁罩。
你可以使用Observatory的堆映射Heap map找到內(nèi)存碎片齐蔽。 此功能顯示用以白色色塊表示空閑內(nèi)存。 如果有許多小白色區(qū)域遍布在有色區(qū)域中床估,則表明應(yīng)用程序的內(nèi)存碎片過多需要優(yōu)化。 有關(guān)更多信息诱渤,請參閱Heap Map丐巫。
5.內(nèi)存泄漏Memory leak
內(nèi)存泄漏memory leak 通常發(fā)生在當(dāng)一個對象處于活動狀態(tài)時(意味著另一個對象指向它)但它沒有被使用(所以它不應(yīng)該有其他對象的引用)時。 這樣的對象不能被垃圾回收勺美,所以它占用堆中的空間并且造成內(nèi)存碎片memory fragmentation递胧。 內(nèi)存泄漏給虛擬機(jī)VM 帶來了不必要的壓力,并且很難調(diào)試赡茸。
6.新一代New generation
新一代New generation 是指大多數(shù)新創(chuàng)建的對象(除非它們非常大)分配在堆的一部分中缎脾。 新一代特別適合臨時和短暫的對象 - 它很小,設(shè)計得很快就能收集垃圾占卧。
7.老一代Old generation
當(dāng)一個對象已經(jīng)存在了一段時間并且在垃圾收集周期中存活下來時遗菠,它通常被提升為堆的一部分,我們稱之為老一代old generation华蜒, 它為新創(chuàng)建的對象釋放新一代辙纬。
Observatory 的heap map 功能為您提供了一種可視化瀏覽老一代old generation的方式。 有關(guān)更多信息叭喜,請參閱Heap Map贺拣。
8.虛擬機(jī)Virtual machine (VM)
Dart虛擬機(jī)是一個可以直接執(zhí)行Dart代碼的軟件。 Dartium瀏覽器是包含Dart VM的特殊版本的Chromium,可以運行Dart代碼譬涡,而無需預(yù)先將其編譯為JavaScript闪幽。
當(dāng)您將Dart web app 編譯為JavaScript時,您可以在任何現(xiàn)代瀏覽器中運行它涡匀。
三盯腌、測試用的本地數(shù)據(jù)庫
Dart通常使用PostgreSQL 數(shù)據(jù)庫。
運行自動化測試的應(yīng)用程序默認(rèn)使用以下配置連接到數(shù)據(jù)庫:
username: dart
password: dart
host: localhost
port: 5432
databaseName: dart_test
在本地安裝PostgreSQL之后渊跋,您可以通過運行以下命令來創(chuàng)建與此連接信息匹配的數(shù)據(jù)庫用戶和數(shù)據(jù)庫:
aqueduct setup
Aqueduct測試會創(chuàng)建一個臨時數(shù)據(jù)庫schema腊嗡,該模式與dart_test數(shù)據(jù)庫中的應(yīng)用程序schema相匹配。 測試完成后拾酝,該數(shù)據(jù)庫中的表格和數(shù)據(jù)將被銷毀燕少。 出于這個原因,在這個數(shù)據(jù)庫中不應(yīng)該創(chuàng)建其他表以避免與測試沖突蒿囤。 Aqueduct測試的這種默認(rèn)行為由test harness提供客们。
四、運行應(yīng)用程序的本地數(shù)據(jù)庫
獨立于測試數(shù)據(jù)庫的本地數(shù)據(jù)庫材诽,用于本地運行應(yīng)用程序底挫。 您可以通過運行psql來打開PostgreSQL終端并運行以下命令來在本地創(chuàng)建數(shù)據(jù)庫:
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;
通過生成并執(zhí)行遷移腳本將您的schema添加到本地數(shù)據(jù)庫:
aqueduct db generate
aqueduct db upgrade --connect postgres://my_local_app_user:mypassword@localhost:5432/my_local_app_db
五、使用本地配置文件
使用配置文件 configuration files 來管理應(yīng)用程序連接到哪個數(shù)據(jù)庫脸侥。 根據(jù)開發(fā)團(tuán)隊的偏好建邓,這可以加入到源代碼管理中。 控制哪個文件使用命令行選項加載到aqueduct serve
或bin/main.dart
腳本中:
aqueduct serve -c local.yaml
六睁枕、根據(jù)情景分配腳本
為了測試客戶端應(yīng)用程序官边,您通常會希望在本地數(shù)據(jù)庫中擁有一組特定的數(shù)據(jù)。 創(chuàng)建bin
腳本以配置數(shù)據(jù)庫并添加所需的數(shù)據(jù)外遇。 例如注簿,您可能有一個名為bin/ios_integration.dart
的腳本,它重新設(shè)置數(shù)據(jù)庫并使用您的應(yīng)用程序中聲明的 Query<T> 實例和ManagedObject<T>向其中插入數(shù)據(jù)跳仿。
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);
});
}