導入:なぜメモリリークの特定が重要なのか
Webアプリケーションを開発していると、「最初は快適に動いていたのに、長時間使っているとだんだん動作が重くなる」という経験はありませんか?これは、不要になったデータがメモリから解放されずに積み重なっていく「メモリリーク」が原因であることが多いです。放置するとブラウザがクラッシュし、ユーザー体験を損なう致命的な問題につながります。今回は、Chromeデベロッパーツールを使って、リアルタイムにメモリの増減を可視化し、リーク箇所を突き止める方法を解説します。
基礎知識:メモリリークとガベージコレクション
まず、ブラウザがメモリを管理する仕組みを理解しましょう。
ガベージコレクション(GC)とは、プログラムが不要になったメモリ領域を自動的に探し出し、解放してくれる仕組みです。しかし、プログラムの中で「まだ使っている」と誤認される状態(参照が残っている状態)だと、GCはメモリを回収できません。これがメモリリークです。ヒーププロファイリングとは、このメモリ(ヒープ)の使用状況を調査し、どのオブジェクトがメモリを占有しているかを分析する手法を指します。
実装/解決策:Allocation Instrumentationの活用
ヒープスナップショットを何度も撮って比較するのは手間がかかりますが、Chromeの「Allocation Instrumentation on timeline」を使えば、メモリの確保と解放のタイミングをタイムライン上で視覚化できます。
手順は以下の通りです。
1. Chromeデベロッパーツールを開き、「Memory」タブを選択します。
2. 「Allocation instrumentation on timeline」を選択し、「Start」ボタンを押します。
3. アプリを操作し、メモリ使用量が増え続けている箇所で記録を停止します。
4. タイムライン上の青いバー(メモリ確保)が、GC後も消えずに積み重なっている箇所を探します。
サンプルプログラム:メモリリークを意図的に発生させる例
以下のコードを実行し、デベロッパーツールでタイムラインを記録してみてください。ボタンを押すたびに配列がグローバル変数に追加され、メモリが解放されなくなります。
// メモリリークを発生させるサンプルコード
let leakArray = []; // グローバル変数にデータを蓄積し続ける
function startLeaking() {
// 100万個の要素を持つ配列を生成してグローバル配列に追加
const data = new Array(1000000).fill('メモリリークのデモ用データ');
leakArray.push(data);
console.log('メモリを確保しました。現在の配列サイズ: ' + leakArray.length);
}
// ボタンを作成して画面に追加する(ブラウザのコンソールで実行可能)
const btn = document.createElement('button');
btn.innerText = 'メモリを消費する';
btn.onclick = startLeaking;
document.body.appendChild(btn);
このコードを実行して記録をとると、タイムライン上に「青いバー(メモリ確保)」が残り続け、GCが動いてもグレーに変わらない(=解放されない)様子が確認できます。
応用・注意点:現場で役立つアドバイス
現場でメモリリークを防ぐための重要なポイントが2つあります。
1つ目は、イベントリスナーの解除です。addEventListenerで登録した関数は、DOM要素を削除してもメモリ上に残り続けることがあります。不要になったら必ずremoveEventListenerを呼び出しましょう。
2つ目は、クロージャの注意です。関数内で定義された変数や関数が、意図せず外部から参照され続けることで、ガベージコレクションの対象から外れてしまうケースが非常に多いです。
メモリリークは「少しずつの蓄積」で発生するため、開発段階から定期的にプロファイリングを行う癖をつけることが、安定したアプリを届けるための近道です。まずは小さなコードから、メモリの動きを観察してみてください。

コメント