ドミネータービューは、Firefox 46 の新機能です。
Firefox 46 より、メモリーツールに新たなビューであるドミネータービューが加わりました。これは、サイトによって割り当てられたオブジェクトの " 保持サイズ " を知るのに役立ちます。保持サイズはオブジェクト自身のサイズと、参照によって保持されているオブジェクトのサイズを合算したものです。
シャローサイズ、保持サイズ、ドミネーターが何かを知っている場合は、ドミネーターの UI のセクションに進んでください。そうでない場合は、ドミネーターの概念の記事でこれらを調べたいと思うかもしれません。
ドミネータの UI
" 表示 " のドロップダウンリストで " ドミネーター " を選択すると、ドミネータービューを表示します。以下のようなものです:
ドミネータービューは 2 つのパネルで構成されます:
- ドミネーターツリーパネルは、スナップショット内でどのノードがもっとも多くのメモリーを保持しているかを表示します。
- 保持パスパネル (Firefox 47 の新機能) は、ひとつのノードに対して 5 つの最短保持パスを表示します。
ドミネーターツリーパネル
ドミネーターツリーは、スナップショット内でどのオブジェクトがもっとも多くのメモリーを保持しているかを表示します。
UI のメインエリアで、最初の行が "GC ルート" という名前になっています。この直下に、以下の項目が並びます:
- すべての GC ルートノード。Gecko では複数のメモリーグラフが存在しますので、ルートも複数あります。多くの (たいていは一時的な) ルートが存在する場合があります。例えば、スタック内で割り当てられた変数はルートになる必要があります。また、内部キャッシュはそれらの要素のルートにする必要があるでしょう。
- 2 つの異なるルートから参照される、他のノード (この場合、どちらのルートもそのノードを支配しません)。
それぞれの項目で、以下の内容を表示します:
- ノードの保持サイズを、バイト数および合計に対するパーセント値で示す
- ノードのシャローサイズを、バイト数および合計に対するパーセント値で示す
- ノードの名前と、メモリー上のアドレス
項目は、メモリーの保持サイズの大きさ順に並びます。例えば:
このスクリーンショットでは、" GC ルート " の配下に項目が 5 つあることがわかります。始めの 2 つは Call オブジェクトと Window オブジェクトであり、それぞれスナップショットの総メモリ量の 21% と 8% を保持しています。また、これらのオブジェクトは " シャローサイズ " が比較的小さく、保持サイズのほぼすべては、支配しているオブジェクト内にあることもわかります。
各 GC ルートの直下に、そのルートが 隣接支配ノード であるすべてのノードを配置します。これらのノードも、保持サイズ順に並びます。
例えば、最初の Window オブジェクトをクリックします:
この Window は CSS2Properties オブジェクトを支配しており、その保持サイズはスナップショット全体の 2% であることがわかります。やはりシャローサイズはとても小さく、保持サイズのほぼすべてが、支配しているノード内にあります。Function の隣にある展開用の三角印をクリックすると、それらのノードを確認できます。
この方法で、スナップショット内でどのオブジェクトがもっとも多くのメモリーを保持しているかを、すばやく把握できます。
Alt + クリックで、ノード配下のグラフ全体を展開できます。
コールスタック
ツール上部のツールバーに、"ラベル" という名称のドロップダウンリストがあります:
デフォルトでは " Type " に設定されています。一方、これを " Call Stack " に切り替えると、コードの中でオブジェクトを割り当てている場所はどこかを表示します。
Firefox 46 では、このオプションの名称は " Allocation Stack " でした。
ビューを表示するには、オブジェクトを割り当てるコードを実行する前に " コールスタックを記録 " のチェックボックスにチェックを入れなければなりません。そしてスナップショットを採取して、" ラベル " ドロップダウンリストで " Call Stack " を選択します。
するとノードを割り当てた関数の名前、およびその関数が存在するファイルの名前、行番号、何文字目かをノードの名前に含めて表示します。ファイル名をクリックすると、デバッガーで該当箇所を表示します。
ここに " (有効なスタックはありません) " と表示される場合があります。特に、現在割り当てスタックはオブジェクトのみ記録しており、配列、文字列、内部構造は記録していません。
保持パスパネル
保持パスパネルではあるノードについて、そのノードから GC ルートに戻る最短パスを 5 つ表示します。これによって、そのノードがガベージコレクションの対象にならないようにしているすべてのノードを知ることができます。オブジェクトがリークしていると思われる場合に、どのオブジェクトが参照を保持しているかを的確に示します。
ドミネータツリーパネルでノードを選択すると、ノードの保持パスを表示します:
ここでは Object を選択しており、GC ルートに戻るパスが 1 つあることがわかります。
GC ルート Window
は HTMLDivElement
オブジェクトへの参照を保持しており、またそのオブジェクトが Object
への参照を保持しています。ドミネーターツリーパネルを見ると、同じパスをたどることができます。これらの参照のどちらかが削除されると、配下のアイテムはガベージコレクションの対象になるでしょう。
グラフ内の各接続に、参照されるオブジェクト用の変数の名称がついています。
ノードから戻る保持パスが複数存在することがあります:
この図では、DocumentPrototype
ノードから GC ルートに戻るパスが 3 つあります。ひとつが削除されても、ほかのパスが維持されていますので DocumentPrototype
はガベージコレクションの対象になりません。
例
シンプルなコードがどのようにドミネータービューへ反映されるかを見ていきましょう。
ここでは monster allocation example を使用します。これは 3 個の配列を生成しており、それぞれに 5,000 体のモンスターが含まれています。また、それぞれのモンスターはランダムに生成された名前を持っています。
スナップショットを採取する
これがドミネータービューでどのように見えるかを確認します:
- ページを読み込みます。
- メモリーツールを有効化していない場合は、オプション で有効化します。
- メモリーツールを開きます。
- "コールスタックを記録" にチェックを入れます。
- "Make monsters!" ボタンを押します。
- スナップショットを採取します。
- " ドミネーター " ビューに切り替えます。
ドミネーターツリーを分析する
上位 3 件の GC ルートが Array であり、それぞれ総メモリー使用量の約 23% を保持しています:
Array を展開すると、含まれているオブジェクト (モンスター) を表示します。それぞれのモンスターは、シャローサイズが 160 バイトと比較的小さくなっています。これは、目の数と触手の数の整数値を含んでいます。また各モンスターの保持サイズは大きく、これはモンスターの名前の文字列が占めています:
これらはすべて、予想したメモリーグラフ に近い形で並んでいます。しかし、ひとつ不思議に思う点があるでしょう。3 つの Array を保持するトップレベルオブジェクトはどこにあるのでしょうか? ある Array の保持パスを確認すると、以下のようになっているでしょう:
ここでは保持するオブジェクトが見えており、またオブジェクト固有の Array は fierce
モンスターの Array です。しかし Array はルートでもあるため、オブジェクトが Array を参照しなくなってもガベージコレクションの対象にはならないでしょう。
これはオブジェクトが Array を支配していないため、ドミネータツリービューに表示されないということです。ドミネータの概念の記事で関連する章をご覧ください。
コールスタックビューを使用する
最後に、Call Stack ビューへ切り替えると、オブジェクトがどこで割り当てられたかを確認できます。また、デバッガーでその場所にジャンプできます: