フロントエンドの現場で、「なぜかカクつく」「スクロールが引っかかる」といったJank(ジャンク)に悩まされた経験はないかな?
CSSのプロパティを調整しても改善しない。そんな時、多くのエンジニアが沼にハマるのが「メモリ管理とガベージコレクション(GC)」の壁だ。今日は、ブラウザの心臓部で起きている、この目に見えない戦いについて話そう。
—
1. なぜGCがレンダリングを「止める」のか?
ブラウザのメインスレッドは、非常に忙しい。HTMLのパース、CSSOMの構築、レイアウト計算、そしてペイント。これら全てを16.6ms(60fps)というシビアな時間内に収める必要がある。
ここで厄介なのが、GC(ガベージコレクション)だ。JavaScriptエンジン(V8など)がメモリを回収する際、安全性を確保するために「Stop-the-world」という現象を引き起こす。つまり、GCが走っている間、ブラウザはJavaScriptの実行を一時停止せざるを得ないんだ。
もし、このGCがメインスレッドの作業中……例えば、アニメーションのフレーム生成中に割り込んできたらどうなるか? 答えは簡単。フレームが落ちる(ドロップする)。ユーザーからすれば、「突然サイトがフリーズした」ように見えるわけだ。
2. メモリリークがレンダリングを腐らせる理由
メモリリークは単に「メモリを食う」だけじゃない。メモリが逼迫すると、ブラウザは血眼になって頻繁にGCを走らせようとする。
- 低負荷時: GCはたまにしか動かない。
- メモリリーク発生時: 空きメモリが少ないため、GCが「今すぐ回収しなきゃ!」と頻繁に割り込む。
結果として、GCの回数が増え、メインスレッドが細切れにブロックされ続ける。これが「じわじわと重くなる」という最悪のユーザー体験を生むんだ。特にDOMノードをJavaScriptの変数に保持したまま、削除し忘れるパターンは、現場で最もよく見る「死のパターン」だよ。
—
3. 実践:GCの悪影響を最小化するコード戦略
では、現場でどう対策すべきか。重要なのは「オブジェクトの使い回し」と「不要な参照の即時解放」だ。
以下のコードは、高頻度でオブジェクトを生成してGCを誘発させてしまう悪い例と、それを防ぐ良い例だ。
/
- 【悪い例】
- スクロールイベント内でオブジェクトを毎回生成している。
- これにより、短命なオブジェクトが大量にヒープに積まれ、
- GCの頻度が跳ね上がる。
/
window.addEventListener(‘scroll’, () => {
const pos = { x: window.scrollX, y: window.scrollY }; // 毎回新規生成!
updateUI(pos);
});
/
- 【良い例:オブジェクトプール的アプローチ】
- 再利用可能な変数を用意し、メモリ確保を抑制する。
/
const scrollPos = { x: 0, y: 0 }; // 外部で一度だけ定義
window.addEventListener(‘scroll’, () => {
// 既存のオブジェクトのプロパティを更新するだけなら、
// 新規メモリ確保が発生せず、GCへの負荷が劇的に下がる。
scrollPos.x = window.scrollX;
scrollPos.y = window.scrollY;
requestAnimationFrame(() => {
updateUI(scrollPos);
});
});
Tips: DOMの参照管理を徹底せよ
特にシングルページアプリケーション(SPA)で、コンポーネントを破棄する際にイベントリスナーやDOMの参照を放置すると、GCはそれを「まだ使われている」と誤解してメモリから消してくれない。
- Reactなら: `useEffect`のクリーンアップ関数で必ずリスナーを削除する。
- バニラJSなら: `null`を代入して参照を明示的に切る。
// コンポーネント破棄時のクリーンアップの例
function setupFeature() {
const element = document.getElementById(‘heavy-widget’);
const handler = () => console.log(‘Clicked’);
element.addEventListener(‘click’, handler);
// 破棄用の関数を返す
return () => {
element.removeEventListener(‘click’, handler);
// 参照を消去し、GCがメモリを回収できるようにする
element = null;
};
}
—
4. 最後に:伝説のアーキテクトからのアドバイス
「メモリを意識しろ」というのは、決してケチケチとメモリを節約しろという意味じゃない。「メインスレッドに余計な仕事をさせるな」ということだ。
ブラウザのレンダリングパイプラインは非常に繊細だ。もし君が開発中に「なんか動きが鈍いな」と感じたら、まずはChrome DevToolsの「Performance」タブを開いてみてほしい。そこで「Garbage Collection」という赤いバーが頻発していないかチェックするんだ。
それが、君のアプリケーションが抱える「目に見えない悲鳴」の正体だよ。
現場のコードは、常に「いかにブラウザに負担をかけず、スムーズに動いてもらうか」という想像力から始まる。この視点を持つだけで、君の書くコードの質は一段上のレベルに達するはずだ。頑張ってくれ!

コメント