パフォーマンスツールのメモリ割り当てビューは、プロファイルの中でページ内のどの関数がもっとも多くのメモリ割り当てを行ったかを表示します。
メモリを大量に割り当てたりメモリ割り当てを多数行ったりするとガベージコレクションを引き起こすため、パフォーマンスの観点で重要です。ガベージコレクションは、ページの応答性を損なう可能性があります。
メモリ割り当てビューは、Firefox 46 の新機能です。
メモリ割り当てビューを有効にするため、プロファイルを記録する前にパフォーマンスツールの設定で "メモリ割り当てを記録" にチェックを入れなければなりません。そして通常どおりプロファイルの記録を行うと、ツールバーに "メモリ割り当て" という新たなタブが現れます:
メモリ割り当てビューを分析する
メモリ割り当てビューは、以下のようなものです:
メモリ割り当てビューは、記録中に行われたメモリ割り当てを定期的にサンプリングします。それぞれの行は、記録中に少なくとも 1 回、メモリ割り当てのサンプルを取得した関数を表します。
各行に、以下の列があります:
- Self Count: 関数内で取得したメモリ割り当てサンプルの数 (合計に対するパーセント値も表示します)
- Self Bytes: 関数内のメモリ割り当てサンプルで割り当てたメモリの総バイト数 (合計に対するパーセント値も表示します)
行は "Self Bytes" 列の値でソートします。
よって、前出の例では以下のことが分かります:
signalLater()
で 8904 個のサンプルを取得しており、これは総サンプル数の 28.57% です。- それらのサンプルで 1102888 バイトのメモリを割り当てており、これはすべてのサンプルが割り当てたメモリの 30.01% です。
それぞれの関数名の隣に、展開用の三角印があります。これをクリックすると、関数を呼び出した場所がわかります:
この例では signalLater()
が 2 つの場所から呼び出されたことが分かります。removeInner()
と setSelectionInner()
です。この方法でコールスタックを戻ることができ、メモリ割り当ての状況をよく理解できます。
Self Cost と Total Cost
このビューでは、列が "Self" と "Total" の 2 つのセットに分けられていることがわかるでしょう。"Self" は、関数自体で取得したサンプルを記録します。"Total" は関数自体と、この関数から呼び出した関数で取得したサンプルを記録します。ビューではツリーの葉に位置する関数をトップレベルに置きます (つまり、コールスタックを反転して表示します) ので、トップレベルではどちらも同じ値になります。一方、コールスタックを戻っていくと違いが出てくることがわかるでしょう:
この図では、signalLater()
で 8904 個のサンプルを取得しています。しかし signalLater()
は、removeInner()
と setSelectionInner()
の 2 か所から呼び出されています。これらの関数はどちらも Self Count が 0 であり、その関数内で直接メモリ割り当ては行っていないことを意味します。一方、removeInner()
の Total Count は 8901、setSelectionInner()
の Total Count はわずか 3 です。これは 8904 回のメモリ割り当てが、removeInner()
の枝で行われた 3回を除き、すべて signalLater()
で発見されたことを語ります。
メモリ割り当てとガベージコレクション
当然ながらサイト内で割り当てたメモリは、知っておくと役に立つ情報です。しかし、サイトのメモリ割り当てのプロファイルとサイトの応答性を主に結びつけるものは、ガベージコレクション (GC) のコストです。
ガベージコレクションを行う JavaScript などの言語は、到達性がなくなったオブジェクトを発見するためにランタイムが定期的にヒープを走査して、そのようなオブジェクトが占めていたメモリを解放しなければなりません。このような GC イベントを実行する間は JavaScript エンジンが一時停止しますので、プログラムが休止して応答性が完全になくなります。
応答性への影響を軽減するため SpiderMonkey (Firefox の JavaScript エンジン) は少しずつ増加する GC を実施でき、GC の合間にプログラムを実行できます。それでも時にはノンインクリメンタルガベージコレクションが必要であり、この場合プログラムは完了するまで待たなければなりません。
GC イベントはタイムラインビューで赤色のマーカーで示され、時には数百ミリ秒続くなど応答性に危険信号がともります:
サイトのパフォーマンスプロファイルに GC イベントがある場合、何ができるのでしょうか? SpiderMonkey は、いつどのようなガベージコレクションを行うかを決めるために複雑なヒューリスティック技術を使用しています。
ただし、通常はメモリ割り当ての圧力 (大量のメモリを割り当てる、または高い頻度でメモリを割り当てる) によって SpiderMonkey がガベージコレクションを行う可能性が高くなり、さらにインクりメンタルではないガベージコレクションを行う可能性も高くなります。
メモリ割り当ての圧力によってガベージコレクションが発生した場合は、タイムラインのマーカーの右側にあるサイドバーで "Show allocation triggers" という名前のリンクを表示します。このリンクをクリックすると開発ツールがメモリ割り当てビューに切り替わり、前回の GC が終了したときからクリックした GC が始まるときまでの時間帯を選択します。これにより、GC イベントを引き起こしたメモリ割り当てを集約して表示します:
このような問題がみられる場合は、そのときに行っているメモリ割り当ての回数や量を削減できないか検討してください。例えば:
- 始めにメモリを割り当てるのではなく、実際必要なときに順次メモリを割り当てられませんか?
- ループ内でメモリを割り当てている場合、ひとつのメモリ領域を毎回の処理で再使用できませんか?