メモリリーク
Memory Leak
メモリリークは、もう必要ないデータのためにメモリを保持し続け、再利用できなくなる問題です。手動の解放漏れだけでなく、GCのある言語でも不要な参照が残ることで起こります。
🐾 猫で例えると?
台の大部分にへそ天の茶トラ、端にはアメショ。この場所取りを、「もう使わないデータが居場所を空けず、次の処理に使える空間が減る」場面に見立てるとメモリリークです。大きく場所を使うこと自体ではなく、使い終わった分まで保持し続けることがポイントです。
💻 プログラミングにおける「メモリリーク」とは?
システムがプログラムを実行する際、データの一時保存用としてメモリ(RAM)の一画を一時的に確保します。通常、その処理が終わればメモリは自動または手動で「解放」され、他の処理が使えるようになります。
しかし、プログラムの設計ミスによって「もう使わないのに、メモリを確保したままシステムに返し忘れる」コードが存在すると、その領域は誰にも使えない無駄なスペースとしてロックされてしまいます。これがメモリリークです。起動直後は問題なくても、システムを長時間稼働させ続けることでジワジワとメモリが蝕まれ、最終的には致命的なメモリ不足エラー(OOM: Out of Memory)を引き起こします。
⚠️ メモリリークの仕組みと注意点
現代の多くの言語(JavaScriptやJava、Pythonなど)には、不要になったメモリを自動で掃除してくれる「ガベージコレクション(GC)」というメンテナンス機能が備わっています。しかし、GCがあるからといって安心はできません。
ガベージコレクションの盲点
JavaScriptのGCでは、到達可能なオブジェクトは利用できる状態として扱われます。循環参照があるだけで必ず回収不能になるわけではありません。長寿命のwindowに登録したリスナーが、不要になった画面のデータを保持している、といった参照経路を調べます。
// ブラウザー:登録と後始末を対にする例
function setupCatPanel() {
const catNames = ["茶トラ", "アメショ"];
function onResize() {
console.log(catNames.length);
}
window.addEventListener("resize", onResize);
return () => window.removeEventListener("resize", onResize);
}
const dispose = setupCatPanel();
// 画面を使い終わったタイミングで呼ぶ
dispose(); この例は、同じ関数参照を使って登録を解除します。解除後にデータが回収可能になるかは、他の参照も残っていないかによります。実際の画面のライフサイクルへ後始末を結び付けることが大切です。
🛠️ メモリリークを調べ、防ぐためのポイント
- ライフサイクルの徹底管理: コンポーネントが破棄されるタイミングで、必ずイベントリスナーの解除(removeEventListener)や、タイマーのクリア(clearInterval)を実行しましょう。
- 同じ操作の前後を比較: 画面を開閉するなど同じ操作を繰り返し、GC後にも不要なオブジェクトが増えて残るかをスナップショットで確認します。キャッシュや一時的な山もあるので、使用量の増加だけでリークと断定しません。
- 弱参照は用途を選ぶ: WeakMapはキーのオブジェクトを、それだけを理由に生存させない関連付けに使えます。ただしタイマーやリスナー、別の強い参照を自動で消すものではありません。
茶トラが場所を使い続けるとアメショの居場所が狭くなるように、不要なデータの保持はメモリを圧迫します。同じ操作を繰り返した後にも使用量が増え続けるかを観察し、残っている参照や解除し忘れた処理を調べましょう。