調查網頁應用程式中的記憶體使用情況可能相當困難。 DevTools 記憶體 工具允許你透過堆積快照來探索網頁應用程式在記憶體中分配的所有物件。 這些資訊對效能調查很有用,因為你可以找出哪些物件佔用最多記憶體。
不過,有時你可能需要專注於 記憶體 工具無法顯示的特定記憶體資料部分。 在這種情況下,使用 DevTools 將整組記憶體資料匯出成 .heapsnapshot JSON 檔案。
本文說明 JSON 檔案的 .heapsnapshot 結構與內容,讓您能自行建立視覺化與分析工具。
記錄堆積快照
要匯出檔案 .heapsnapshot ,首先你需要在 記憶體 工具中錄製堆積快照,如下:
在 Microsoft Edge 中,導向你想匯出資料的網站。
在 Windows、Linux) 按 Ctrl+Shift+I (macOS) 按 Command+Option+I (開啟開發工具。
打開 記憶 工具。
選擇 堆積快照 ,然後點選 「擷取快照」。
欲了解更多資訊,請參閱 使用記憶體工具記錄堆積快照 (「堆積快照」剖析類型) 。
匯出並檢視 .heapsnapshot 檔案
錄製完堆積快照後,就可以匯出。
在 記憶體 工具左側邊欄,點選你剛錄製的堆積快照項目旁的 儲存 。
將檔案副檔名
.heapsnapshot從 改為.json,以便更容易在文字編輯器中開啟檔案。在文字編輯器(如 Visual Studio Code)中開啟已儲存的檔案。
為了讓 JSON 更易讀,在 Visual Studio Code 中,右鍵點擊程式碼中任意位置,然後選擇「格式化文件」。
通常每次錄製並匯出堆積快照時,最終檔案 .heapsnapshot 都會不同。 堆積快照是根據目前在 DevTools 中檢查的網頁應用程式內容動態產生的。
檔案格式概述.heapsnapshot
網頁應用程式所使用的記憶體由 V8 以圖形形式組織,V8 是 Microsoft Edge 使用的 JavaScript 引擎。 圖是一種資料型態,由圖) 上的 節點 (點,以及) 點間的 邊 (連結所組成。
檔案中的 .heapsnapshot 資料代表網頁應用程式的記憶體,該圖能有效繪製,並方便在瀏覽器程序與 DevTools 之間傳輸資料群組。 該 .heapsnapshot 檔案包含節點與邊之間關係的扁平表示,作為包含數字與字串陣列的 JSON 物件。 該檔案具有 .heapsnapshot 副檔名,並包含 JSON 格式的資料。
數據主要分為兩部分:
- 元資料包含解析代表記憶體圖資料陣列所需的所有資訊。
- 陣列資料,包含重建圖所需的實際資料。
更新此資料格式文件
檔案格式 .heapsnapshot 如下所述,隨著 V8 與 DevTools 的演進可能會改變。 如果你發現文件中有差異,請在 MicrosoftDocs 或 edge-developer 倉庫中提供回饋。
資料結構.heapsnapshot
頂層結構
JSON .heapsnapshot 資料包含一個根物件,具有以下屬性:
{
"snapshot": {},
"nodes": [],
"edges": [],
"trace_function_infos": [],
"trace_tree": [],
"samples": [],
"locations": [],
"strings": []
}
| 屬性 | 描述 | 格式 |
|---|---|---|
snapshot |
包含關於記憶體圖資料格式及其大小的所有資訊。 | Object |
nodes |
所有重建圖節點所需的資訊。 要解析這些資料,請使用 snapshot.meta.node_types 和 snapshot.meta.node_fields。 |
Array |
edges |
所有重建圖邊所需的資訊。 要解析這些資料,請使用 snapshot.meta.edge_types 和 snapshot.meta.edge_fields。 |
Array |
trace_function_infos |
還沒正式記錄 | Array |
trace_tree |
還沒正式記錄 | Array |
samples |
還沒正式記錄 | Array |
locations |
包含節點腳本位置的資訊。 解析這些資料時,請使用 snapshot.meta.location_fields 與陣列一起 nodes 。 |
Array |
strings |
一個包含所有儲存在記憶體中的字串陣列。 這些字串可以是任何字串,例如使用者定義的字串或程式碼。 | Array |
快照
{
"snapshot": {
"meta": {},
"node_count": 123,
"edge_count": 456,
"trace_function_count": 0
}
...
}
| 屬性 | 描述 | 格式 |
|---|---|---|
meta |
包含記憶體圖資料中每個物件形狀與大小的屬性。 | Object |
node_count |
記憶體圖中的節點總數。 | Number |
edge_count |
記憶體圖中邊的總數。 | Number |
trace_function_count |
記憶體圖中追蹤函數的總數。 | Number |
快照中繼資料
{
"snapshot": {
"meta": {
"node_fields": [],
"node_types": [],
"edge_fields": [],
"edge_types": []
}
}
...
}
| 屬性 | 描述 | 格式 |
|---|---|---|
node_fields |
列出重建節點所需的所有屬性。 | Array |
node_types |
重建節點所需的所有屬性類型。 型態的數量與定義在 中的 node_fields性質數量相同。 |
Array |
edge_fields |
列出重建邊所需的所有屬性。 | Array |
edge_types |
所有重建邊緣所需的屬性類型。 型態的數量與 中的 edge_fields性質數量相同。 |
Array |
以下是一個元資料物件的範例:
{
"snapshot": {
"meta": {
"node_fields": [
"type",
"name",
"id",
"self_size",
"edge_count",
"trace_node_id",
"detachedness"
],
"node_types": [
[
"hidden",
"array",
"string",
"object",
"code",
"closure",
"regexp",
"number",
"native",
"synthetic",
"concatenated string",
"sliced string",
"symbol",
"bigint",
"object shape"
],
"string",
"number",
"number",
"number",
"number",
"number"
],
"edge_fields": [
"type",
"name_or_index",
"to_node"
],
"edge_types": [
[
"context",
"element",
"property",
"internal",
"hidden",
"shortcut",
"weak"
],
"string_or_number",
"node"
]
}
}
}
Nodes
nodes陣列位於資料的最.heapsnapshot頂層,包含重建記憶體圖節點所需的所有資訊。
解析此陣列需提供以下資訊:
-
snapshot.node_count,以判斷節點數量。 -
snapshot.meta.node_fields,以判斷每個節點有多少欄位。
陣列中的每個節點由一串 snapshot.meta.node_fields.length 數字表示。 因此,陣列中 nodes 元素總數乘 snapshot.node_count 以 snapshot.meta.node_fields.length。
要重建節點,請從陣列中依大小snapshot.meta.node_fields.length為 的群組讀取數字nodes。
以下程式碼片段顯示 node_fields 圖中前兩個節點的元資料與資料:
{
"snapshot": {
"meta": {
"node_fields": [
"type",
"name",
"id",
"self_size",
"edge_count",
"trace_node_id",
"detachedness"
]
...
}
...
},
"nodes": [
9,1,1,0,10,0,0,
2,1,79,12,1,0,0,
...
]
...
}
| 節點群中的指標 | 名稱 | 描述 |
|---|---|---|
0 |
type |
節點的類型。 請參見下方的 節點類型。 |
1 |
name |
節點名稱。 這是一個頂層 strings 陣列中的索引。 要找到實際名稱,請使用索引號在頂層 strings 陣列中查找該字串。 |
2 |
id |
節點的唯一 ID。 |
3 |
self_size |
節點大小(位元組)。 |
4 |
edge_count |
連接到該節點的邊數。 |
5 |
trace_node_id |
追蹤節點的識別碼 |
6 |
detachedness |
是否能從全域物件存取 window 此節點。
0 表示該節點並非分離;節點可由 window 全域物件存取。
1 表示節點是分離的;節點無法從 window 全域物件存取。 |
節點類型
陣列中節點 nodes 的數字組中的第一個數字對應其類型。 這個數字是一個索引,可以用來查詢陣列中的 snapshot.meta.node_types[0] 型別名稱。
| 節點類型 | 描述 |
|---|---|
| 隱藏 | V8 內部元素,且不直接對應使用者可控制的 JavaScript 物件。 在 DevTools 中,這些都顯示在系統類別名稱 () 。 即使這些物品是內部的,它們仍可能是保留者路徑的重要部分。 |
| 物件 | 任何使用者定義的物件,例如 { x: 2 } 或 new Foo(4)。 Context(在 DevTools 中以 系統/Context 形式顯示)會保留那些必須配置在堆積堆上的變數,因為它們是巢狀函式使用。 |
| 原始 | 這些東西是由 Blink 渲染引擎分配的,而不是 V8 分配的。 這些大多是DOM項目,例如 HTMLDivElement 或 CSSStyleRule。 |
| 串接字串 | 這是將兩串 + 字串與運算子串接的結果。 V8 不是建立一個包含兩個來源字串所有資料的全新字串,而是建立 ConsString 一個包含指向兩個來源字串指標的物件。 從 JavaScript 的角度來看,它和其他字串一樣,但從記憶體剖析的角度來看,它就不同了。 |
| 切片串 | 子字串操作的結果,例如使用 String.prototype.substr 或 String.prototype.substring。 V8 避免複製字串資料,而是建立 SlicedString一個指向原始字串並指定起始索引與長度的 。 從 JavaScript 的角度來看,切片字串的行為與其他字串無異,但從記憶體剖析的角度來看,則有所不同。 |
| 陣列 | 各種內部清單,會在 DevTools 中以類別名稱 顯示 (陣列) 。 像 Hidden 一樣,這個類別將各種事物歸類在一起。 這裡許多物件都以 (物件屬性 命名,) 或 (物件元素) ,表示它們包含 JavaScript 物件的字串鍵或數字鍵特性。 |
| 代碼 | 這些東西會隨著腳本數量和/或該函式執行次數成比例成長。 |
| 人造的 | 合成節點並不對應記憶體中實際分配的部分。 這些用來區分不同類型的垃圾回收 (GC根) 根。 |
邊緣
與陣列類似,nodesedges頂層陣列包含重建記憶體圖邊緣所需的所有元素。
與節點類似,邊的總數可透過 snapshot.edge_count 乘以 snapshot.meta.edge_fields.length來計算。 邊也會以一串數字儲存,你需要依大小 snapshot.meta.edge_fields.length為 的群組來迭代。
然而,要正確讀取 edges 陣列,首先需要讀取陣列, nodes 因為每個節點都知道它有多少條邊。
要重現一條邊,你需要三個資訊:
- 邊緣型。
- 邊緣名稱或索引。
- 邊連接的節點。
例如,如果你讀取陣列中的nodes第一個節點,且其edge_count屬性設為 4,那麼陣列中的edges前四組snapshot.meta.edge_fields.length數字對應該節點的四條邊。
| 邊群中的指標 | 名稱 | 描述 |
|---|---|---|
0 |
type |
那種銳利的感覺。 請參閱 邊緣類型 以了解可能的類型。 |
1 |
name_or_index |
這可以是一個數字或一個字串。 如果是數字,則對應頂層 strings 陣列中的索引,該邊的名稱就在那裡。 |
2 |
to_node |
該邊所連接的陣列中的 nodes 索引。 |
邊型
陣列中邊 edges 的數字組中的第一個數字對應其類型。 這個數字是一個索引,可以用來查詢陣列中的 snapshot.meta.edge_types[0] 型別名稱。
| 邊型 | 描述 |
|---|---|
| 內部 | 這些邊不對應 JavaScript 可見的名稱,但仍很重要。 舉例來說,函式實例有一個「上下文」,代表函式定義在作用域內的變數狀態。 JavaScript 程式碼無法直接讀取函式的「上下文」,但在調查保留器時需要這些邊緣。 |
| 弱 | 弱邊無法讓它們所連接的節點存活,因此在Retainers的視角中被省略。 任何只有弱邊指向的物件,都可以被垃圾回收 (GC) 丟棄。 |
| 隱藏 | 類似於內部,但這些邊沒有唯一名稱,而是依遞增順序編號。 |
| Shortcut | 一個更易讀的路徑呈現。 這種類型很少使用。 例如,如果你用 Function.prototype.bind 來建立一個包含某些束縛參數的綁定函數,V8 會產生JSBoundFunctionFixedArray一個指向一個 (,一個內部型別) ,指向每個綁定參數。 在產生快照時,V8 會直接從綁定函數加上一條捷徑邊到每個綁定參數,繞過 FixedArray。 |
| 元素 | 物件屬性,其中鍵值是數字。 |
地點
locations陣列位於資料的最.heapsnapshot頂層,包含快照中部分節點建立位置的資訊。 此陣列由一串數字組成,設計給大小 snapshot.meta.location_fields.length為 的群組讀取。 因此,我們會去 snapshot.meta.location_fields 了解陣列中 locations 每個位置有多少欄位,以及這些欄位是什麼。 例如,若 location_fields 包含 4 個項目,則應由 4 個組成的群組讀取該 locations 陣列。
snapshot.meta.location_fields 包含各地區的資訊:
索引 location_fields |
名稱 | 描述 |
|---|---|---|
0 |
object_index |
與該位置相關聯的陣列節點 snapshot.nodes 索引。 |
1 |
script_id |
建立相關節點的腳本 ID。 |
2 |
line |
節點建立的行號,也就是建立該節點的腳本。 |
3 |
column |
節點建立的欄位編號,也就是建立該節點的腳本中。 |
以下程式碼範例說明如何將陣列連結 snapshot.locations 到陣 snapshot.nodes 列:
{
"snapshot": {
"meta": {
"location_fields": [
"object_index",
"script_id",
"line",
"column"
]
...
}
...
},
"nodes": [
9,1,1,0,10,0,0,
2,1,79,12,1,0,0,
...
],
"locations":[
7,9,0,0,
113792,3,25,21,
...
],
...
}
陣列中的 locations 第一個位置為 7,9,0,0,。 這個位置對應到陣列中從索引 7 nodes 開始的節點資訊群組。 因此,節點包含以下鍵值對:
"type": 2,
"name": 1,
"id": 79,
"self_size": 12,
"edge_count": 1,
"trace_node_id": 0,
"detachedness": 0,
"script_id": 9,
"line" 0,
"column": 0,
另請參閱
想了解更多關於檔案格式的.heapsnapshot資訊,請參閱產生該檔案的程式碼,也就是 HeapSnapshotGenerator 。heap-snapshot-generator.h