ヒープスナップショットファイル形式
Web アプリケーションでのメモリ使用量の調査は困難な場合があります。 DevTools Memory ツールを使用すると、ヒープをスナップショットすることで、Web アプリケーションによってメモリに割り当てられているすべてのオブジェクトを探索できます。 この情報は、最も多くのメモリを消費しているオブジェクトを確認できるため、パフォーマンスの調査に役立ちます。
ただし、 メモリ ツールに 表示されないメモリ データの特定の部分に焦点を当てる必要がある場合があります。 この場合は、DevTools を使用して、メモリ データのセット全体を .heapsnapshot
JSON ファイルとしてエクスポートします。
この記事では、独自の視覚化と分析ツールを構築できるように、JSON ファイルの .heapsnapshot
構造と内容について説明します。
ヒープ スナップショットを記録する
ファイルを.heapsnapshot
エクスポートするには、まず、次のように、メモリ ツールでヒープ スナップショットを記録する必要があります。
Microsoft Edge で、データをエクスポートする Web サイトに移動します。
Ctrl + Shift + I (Windows、Linux) または Command + Option + I (macOS) を押して Devtools を開きます。
メモリ ツールを開きます。
[ヒープ スナップショット] を選択し、[スナップショットの取得] をクリックします。
詳細については、「メモリ ツールを使用してヒープ スナップショットを記録する ("ヒープ スナップショット" プロファイリングの種類)」を参照してください。
ファイルのエクスポートと表示.heapsnapshot
ヒープ スナップショットを記録したら、それをエクスポートできます。
メモリ ツールの左側のサイドバーで、記録したヒープ スナップショット項目の横にある [保存] をクリックします。
ファイル拡張子を から
.heapsnapshot
に.json
変更すると、テキスト エディターでファイルを簡単に開くことができます。Visual Studio Code などのテキスト エディターで保存したファイルを開きます。
JSON を読みやすくするには、Visual Studio Code でコード内の任意の場所を右クリックし、[ ドキュメントの書式設定] を選択します。
通常、結果.heapsnapshot
のファイルは、ヒープ スナップショットを記録してエクスポートするたびに異なります。 ヒープ スナップショットは、DevTools で現在検査されている Web アプリケーションの内容に基づいて動的に生成されます。
ファイル形式の .heapsnapshot
概要
Web アプリケーションによって使用されるメモリは、Microsoft Edge によって使用される JavaScript エンジンである V8 によってグラフとして編成されます。 グラフは、 ノード (グラフ上のポイント) と エッジ (ポイント間のリンク) で構成されるデータ型です。
ファイル内の .heapsnapshot
データは、効率的にグラフ化する Web アプリのメモリを表し、ブラウザー プロセスと DevTools の間でデータのグループを転送しやすくなります。 ファイル .heapsnapshot
には、数値と文字列の配列を含む JSON オブジェクトとして、ノードとエッジ間の関係のフラット化された表現が含まれています。 ファイルにはファイル名拡張子があり .heapsnapshot
、JSON 形式のデータが含まれています。
データには、次の 2 つのメイン部分があります。
- メモリ グラフを表すデータの配列を解析するために必要なすべての情報を含むメタデータ。
- 配列データ。グラフの再作成に必要な実際のデータが含まれます。
このデータ形式のドキュメントの更新
以下に .heapsnapshot
記載されているように、ファイルの形式は、V8 と DevTools の進化に伴って変更される可能性があります。 ドキュメントに不一致が見つかる場合は、 MicrosoftDocs/edge-developer リポジトリでフィードバックを提供してください。
データの .heapsnapshot
スキーマ
最上位レベルの構造
.heapsnapshot
JSON データには、次のプロパティを持つルート オブジェクトが含まれています。
{
"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
最初の 2 つのノードのメタデータとデータを示しています。
{
"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 |
トレース ノードの ID |
6 |
detachedness |
このノードにグローバル オブジェクトから window 到達できるかどうか。
0 ノードがデタッチされていないことを意味します。ノードはグローバル オブジェクトから window 到達できます。
1 ノードがデタッチされていることを意味します。ノードにグローバル オブジェクトから window 到達できません。 |
ノードの種類
配列内のノードの数値グループの最初の nodes
数値は、その型に対応します。 この番号は、配列内 snapshot.meta.node_types[0]
の型名を検索するために使用できるインデックスです。
ノードの種類 | 説明 |
---|---|
Hidden | ユーザーが制御できる JavaScript オブジェクトに直接対応しない V8 内部要素。 DevTools では、これらはすべてカテゴリ名 (システム) の下に表示されます。 これらのオブジェクトは内部的であっても、リテーナー パスの重要な部分になる可能性があります。 |
オブジェクト | または new Foo(4) などの{ x: 2 } ユーザー定義オブジェクト。 DevTools に システム/コンテキストとして表示されるコンテキストは、入れ子になった関数によって使用されるため、ヒープに割り当てる必要があった変数を保持します。 |
ネイティブ | V8 ではなく、Blink レンダリング エンジンによって割り当てられるもの。 これらは主に、 や CSSStyleRule などの HTMLDivElement DOM 項目です。 |
連結された文字列 | 演算子と 2 つの文字列を連結した + 結果。 V8 は、2 つのソース文字列のすべてのデータのコピーを含む新しい文字列を作成するのではなく、2 つのソース文字列へのポインターを含むオブジェクトを作成 ConsString します。 JavaScript の観点からは、他の文字列と同様に動作しますが、メモリ プロファイリングの観点からは異なります。 |
スライスされた文字列 | または String.prototype.substr String.prototype.substring などの部分文字列操作の結果。 V8 では、元の文字列を指し示し、開始インデックスと長さを指定する を作成 SlicedString することで、文字列データのコピーを回避します。 JavaScript の観点から見ると、スライスされた文字列は他の文字列と同様に機能しますが、メモリ プロファイリングの観点からは異なります。 |
配列 | カテゴリ名 (配列) を使用して DevTools に表示されるさまざまな内部リスト。 非表示と同様に、このカテゴリはさまざまなものをグループ化します。 ここでのオブジェクトの多くは、名前 (オブジェクト プロパティ) または (オブジェクト要素) であり、JavaScript オブジェクトの文字列キー付きプロパティまたは数値キー付きプロパティが含まれていることを示します。 |
コード | スクリプトの量や関数の実行回数に比例して増加するもの。 |
人造 | 合成ノードは、メモリ内で実際に割り当てられたものには対応しません。 これらは、さまざまな種類のガベージ コレクション (GC) ルートを区別するために使用されます。 |
エッジ
配列と nodes
同様に、最上位の edges
配列には、メモリ グラフの端を再作成するために必要なすべての要素が含まれています。
ノードと同様に、エッジの合計数は を乗算snapshot.edge_count
snapshot.meta.edge_fields.length
して計算できます。 エッジは数値のシーケンスとしても格納されます。これは、サイズ snapshot.meta.edge_fields.length
のグループで反復処理する必要があります。
ただし、配列を edges
正しく読み取るために、各ノードは配列のエッジの数を nodes
認識するため、最初に配列を読み取る必要があります。
エッジを再作成するには、次の 3 つの情報が必要です。
- エッジの種類。
- エッジ名またはインデックス。
- エッジが接続されているノード。
たとえば、配列内の最初のnodes
ノードを読み取り、そのedge_count
プロパティが 4 に設定されている場合、配列内edges
の最初の snapshot.meta.edge_fields.length
4 つの数値グループは、このノードの 4 つのエッジに対応します。
エッジ グループのインデックス | 名前 | 説明 |
---|---|---|
0 |
type |
エッジの種類。 可能 な型については、「Edge 型 」を参照してください。 |
1 |
name_or_index |
数値または文字列を指定できます。 数値の場合は、最上位の strings 配列のインデックスに対応します。このインデックスは、エッジの名前を見つけることができます。 |
2 |
to_node |
このエッジが nodes 接続されている配列内のインデックス。 |
エッジの種類
配列内のエッジ edges
の数値グループの最初の数値は、その型に対応します。 この番号は、配列内 snapshot.meta.edge_types[0]
の型名を検索するために使用できるインデックスです。
エッジの種類 | 説明 |
---|---|
内部 | JavaScript で表示される名前に対応していないが、依然として重要なエッジ。 たとえば、関数インスタンスには、関数が定義されたスコープ内にあった変数の状態を表す "コンテキスト" があります。 JavaScript コードが関数の "コンテキスト" を直接読み取る方法はありませんが、リテーナーを調査するときは、これらのエッジが必要です。 |
弱い | 弱いエッジは、接続されているノードを維持しないため、保持ビューから省略されます。 弱いエッジのみを指すオブジェクトは、ガベージ コレクション (GC) によって破棄できます。 |
Hidden | 内部に似ていますが、これらのエッジには一意の名前がなく、代わりに番号が増える順序で番号が付けられます。 |
ショートカット | 他のパスの読みやすい表現。 この型はまれに使用されます。 たとえば、 を使用Function.prototype.bind してバインドされた関数をいくつかのバインドされた引数で作成する場合、V8 は、バインドされた各引数をFixedArray 指す (内部型) を指す を作成JSBoundFunction します。 スナップショットを生成する場合、V8 は バインドされた関数のショートカット エッジをバインドされた各引数に直接追加し、 をバイパスしますFixedArray 。 |
要素 | キーが数値であるオブジェクト プロパティ。 |
locations
locations
データの.heapsnapshot
最上位にある配列には、スナップショット内のノードの一部が作成された場所に関する情報が含まれています。 この配列は、サイズ snapshot.meta.location_fields.length
のグループによって読み取られる一連の数値で構成されます。 したがって、配列内locations
の各場所に含まれるフィールドの数と、それらのフィールドが何であるかを知るために移動snapshot.meta.location_fields
します。 たとえば、4 つの項目が含まれている場合 location_fields
、 locations
配列は 4 のグループで読み取られます。
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
参照してください。