メインコンテンツまでスキップ

無料でガベージコレクションを実現

· 約12分
ハンネス・ペイヤーとロス・マクイルロイ、アイドルガベージコレクターズ

JavaScriptのパフォーマンスはChromeの価値の重要な側面の1つであり、特にスムーズな体験を実現する上で重要です。Chrome 41以降、V8は新しい技術を利用して、アイドル時間内の小さな未使用時間に高コストなメモリ管理操作を隠すことで、Webアプリケーションの応答性を向上させています。この結果、Web開発者はガベージコレクションによるジャンクが大幅に軽減され、スムーズなスクロールや滑らかなアニメーションを期待することができます。

ChromeのV8 JavaScriptエンジンなどのモダンな言語エンジンの多くは、開発者自身がメモリ管理を心配する必要がないように、実行中のアプリケーションのメモリを動的に管理します。このエンジンは、アプリケーションに割り当てられたメモリを定期的に精査し、不要なデータを判別してそれをクリアすることで、空き容量を確保します。このプロセスはガベージコレクションとして知られています。

Chromeでは、スムーズな60フレーム/秒(FPS)のビジュアル体験を提供することを目指しています。V8はすでにガベージコレクションを小さなチャンクで行おうとしていますが、より大きなガベージコレクション操作が予測できないタイミングで発生することがあります。これにはアニメーションの途中で実行を一時停止し、Chromeがその60 FPSの目標に到達できない原因となる場合も含まれます。

Chrome 41にはBlinkレンダリングエンジンのタスクスケジューラが含まれており、レイテンシーに敏感なタスクを優先することで、Chromeの応答性を確保しています。このタスクスケジューラは作業の優先順位付けを可能にするだけでなく、システムがどれくらい忙しいか、どのタスクを実行する必要があるのか、各タスクの緊急度に関する集中した情報を持っています。そのため、Chromeがアイドル状態になる可能性が高い時期や、どのくらいアイドル状態が続くかを大まかに推測することができます。

例えば、Chromeがウェブページ上でアニメーションを表示している場合、アニメーションは60 FPSで画面を更新し、Chromeには更新を行うための約16.6ミリ秒が与えられます。その結果、前のフレームが表示された直後に現在のフレームの作業を開始し、この新しいフレームの入力、アニメーション、およびフレームレンダリングタスクを実行します。Chromeがこの作業を16.6ミリ秒未満で完了すると、次のフレームのレンダリングを開始する準備ができるまでの残りの時間には何もする必要がなくなります。Chromeのスケジューラにより、V8はこの_アイドル期間_を利用して、Chromeがアイドル状態になると特別な_アイドルタスク_をスケジュールできるようになります。

図1: アイドルタスクによるフレームレンダリング

アイドルタスクは低優先度の特別なタスクであり、スケジューラがアイドル期間中であると判断した場合に実行されます。アイドルタスクには、スケジューラがアイドル状態が続くと推測した時間が期限として与えられます。図1のアニメーションの例では、次のフレームの描画が開始されるべき時点が期限になります。他の状況(例えば、スクリーン上での活動がない場合)では、次の保留中のタスクが実行される予定の時点が期限になる可能性もあります。ただし、Chromeが予期しないユーザー入力に応答できるようにするために、上限を50ミリ秒に設定しています。この期限は、アイドルタスクがジャンクや入力応答の遅延を引き起こさない範囲でどれだけ作業ができるかを推測するために使用されます。

アイドルタスク内で行われるガベージコレクションは、重要でレイテンシーに敏感な操作から隠されています。これにより、これらのガベージコレクションタスクは「無料」で行われます。V8がこれをどのように実現しているかを理解するために、V8の現在のガベージコレクション戦略をレビューする価値があります。

V8のガベージコレクションエンジンの詳細

V8は世代別ガベージコレクターを使用しており、JavaScriptヒープを新しく割り当てられたオブジェクト用の小さな若い世代と、長期間生存するオブジェクト用の大きな古い世代に分割しています。ほとんどのオブジェクトは短命であるため、この世代別戦略により、ガベージコレクターは若い世代(スカベンジと呼ばれる)で定期的かつ短時間のガベージコレクションを実行でき、古い世代内のオブジェクトを追跡する必要がなくなります。

若い世代ではセミスペース割り当て戦略を使用します。つまり、新しいオブジェクトは最初に若い世代のアクティブセミスペースに割り当てられます。このセミスペースが満杯になると、スカベンジ操作が実行され、生存オブジェクトが別のセミスペースに移動されます。一度移動されたオブジェクトは古い世代に昇格され、長寿命であると見なされます。生存オブジェクトが移動された後、新しいセミスペースがアクティブになり、古いセミスペースに残っているデッドオブジェクトは破棄されます。

したがって、若い世代のスカベンジの所要時間は、若い世代内の生存オブジェクトのサイズに依存します。若い世代内のほとんどのオブジェクトが到達不能になる場合、スカベンジは非常に高速に終了します(<1 ms)。しかし、ほとんどのオブジェクトがスカベンジを生き延びる場合、スカベンジにかかる時間はかなり長くなる可能性があります。

古い世代での生存オブジェクトのサイズがヒューリスティックに算出された制限を超えると、ヒープ全体の主要なコレクションが実行されます。古い世代では、レイテンシとメモリ消費を最適化するためのいくつかの最適化を備えたマーク・アンド・スイープ法コレクターを使用します。マークのレイテンシはマークする必要がある生存オブジェクトの数に依存し、大規模なWebアプリケーションではヒープ全体をマークするのに100msを超える可能性があります。このような長時間メインスレッドを停止させないために、V8ではインクリメント的に生存オブジェクトを小さなステップでマークすることが長らく可能であり、各マークステップが5ms以下に収まるよう努めています。

マーク後、古い世代のメモリ全体をスイープしてアプリケーションに再利用可能なメモリを提供します。この作業は専用のスイーパースレッドによって並行して行われます。最後に、古い世代でメモリの断片化を減らすためのメモリ圧縮が行われます。このタスクは非常に時間がかかる可能性があり、メモリ断片化が問題となる場合にのみ実行されます。

まとめると、主なガベージコレクションタスクは以下の4つです:

  1. 通常高速な若い世代のスカベンジ
  2. インクリメンタルマーカーによるマークステップ、これはステップサイズに依存して任意の長さになることがあります
  3. 長時間かかる可能性のあるフルガベージコレクション
  4. 積極的なメモリ圧縮を伴うフルガベージコレクション、これは長時間かかる可能性がありますが、断片化されたメモリをクリーンアップします

これらの操作をアイドル期間中に実行するために、V8はスケジューラーにガベージコレクションのアイドルタスクを投稿します。これらのアイドルタスクが実行されるとき、期限を守りながらメモリ消費を減らし、将来のフレームレンダリングや入力遅延の問題を回避するためにどのガベージコレクションタスクを行うべきかをV8のガベージコレクションアイドルタイムハンドラーが評価します。

アプリケーションの割り当て率を測定した結果、次のアイドル期間が来る前に若い世代がいっぱいになる可能性がある場合、ガベージコレクターはアイドルタスク中に若い世代のスカベンジを実行します。また、最近のスカベンジタスクにかかった平均時間を計算して、将来のスカベンジの所要時間を予測し、アイドルタスクの期限を守れるようにします。

古い世代の生存オブジェクトのサイズがヒープ制限に近づくと、インクリメンタルマークが開始されます。インクリメンタルマークステップはマークすべきバイト数によって線形的に調整可能です。平均測定されたマーク速度に基づき、ガベージコレクションアイドルタイムハンドラーは、与えられたアイドルタスク内でできるだけ多くのマーク作業を収めるよう努めます。

古い世代がほぼ満杯で、タスクに提供される期限がコレクションを完了するのに十分長いと見積もられる場合、フルガベージコレクションがアイドルタスク中にスケジュールされます。コレクションの一時停止時間は、マーク速度と割り当てオブジェクトの数を掛け合わせたものに基づいて予測されます。さらに圧縮を伴うフルガベージコレクションは、ウェブページがかなりの時間アイドル状態であった場合にのみ実行されます。

パフォーマンス評価

アイドル時間中にガベージコレクションを実行する影響を評価するために、ChromeのTelemetryパフォーマンスベンチマークフレームワークを使用して、人気のあるウェブサイトがロード中にどれだけスムーズにスクロールするかを評価しました。Linuxワークステーション上でトップ25サイト、およびAndroid Nexus 6スマートフォン上の典型的なモバイルサイトでベンチマークを行い、どちらも人気のあるウェブページ(Gmail、Googleドキュメント、YouTubeなどの複雑なウェブアプリを含む)を開き、その内容を数秒間スクロールしました。Chromeはスムーズなユーザー体験のために60FPSでのスクロールを目指しています。

図2は、アイドル時間中にスケジュールされたガベージコレクションの割合を示しています。ワークステーションの高速なハードウェアは、Nexus 6と比較して全体のアイドル時間が増加し、それによりアイドル時間中にスケジュールされるガベージコレクションの割合が高くなります(Nexus 6の31%に対して43%)。その結果、ジャンクメトリックで約7%の改善が見られました。

図2: アイドル時間中に発生するガーベッジコレクションの割合

ページのレンダリングの滑らかさを向上させるだけでなく、これらのアイドル期間は、ページが完全にアイドル状態になると、より積極的にガーベッジコレクションを実行する機会を提供します。Chrome 45での最近の改善により、アイドル状態の前景タブが消費するメモリ量が大幅に削減されます。図3は、Chrome 43での同じページと比較して、GmailのJavaScriptヒープのメモリ使用量がアイドル状態になると約45%削減される様子を示しています。

図3: 最新版Chrome 45でのGmailのメモリ使用量(左) vs. Chrome 43

これらの改善は、費用のかかるガーベッジコレクション操作が実行されるタイミングをより賢く計画することで、ガーベッジコレクションの停止を隠すことが可能であることを示しています。Web開発者は、滑らかな60FPSアニメーションを目指す場合でも、もはやガーベッジコレクションの停止を恐れる必要はありません。ガーベッジコレクションのスケジューリングの限界をさらに押し広げるにつれて、さらなる改善にご期待ください。