メモリリーク
Memory Leak
プログラムが動的に確保したメモリ領域の解放を忘れることで、利用可能な空き容量が徐々に減っていく現象。放置するとメモリが枯渇し、最終的にシステムがクラッシュしたり動作が極端に重くなったりします。
🐾 猫で例えると?
茶トラが台の上で豪快にへそ天をしてスペースを占有したままどかないため、アメショが端っこでギリギリ耐えています。使い終わったはずの古い領域がいつまでも居座り続け、本当に必要な処理のためのスペース(メモリ)がどんどん削られていく状態がメモリリークです。
🐾猫あるある:IT現場の日常
- 遊んだおもちゃを部屋中に放置して去る:確保したメモリ領域が解放されないまま放置され、利用可能な空きリソースが徐々に減少していく現象。
- 食べ終えた皿が片付かず次の皿が置けない:不要になったデータが領域を占拠し続け、新しいオブジェクトのためのメモリ割り当て要求が失敗する状態。
- 毛づくろいをサボり抜け毛をまき散らす:ガベージコレクションによる不要リソースの回収が追いつかず、プロセスのメモリ消費量が肥大化していく様子。
💻 プログラミングにおける「メモリリーク」とは?
システムがプログラムを実行する際、データの一時保存用としてメモリ(RAM)の一画を一時的に確保します。通常、その処理が終わればメモリは自動または手動で「解放」され、他の処理が使えるようになります。
しかし、プログラムの設計ミスによって「もう使わないのに、メモリを確保したままシステムに返し忘れる」コードが存在すると、その領域は誰にも使えない無駄なスペースとしてロックされてしまいます。これがメモリリークです。起動直後は問題なくても、システムを長時間稼働させ続けることでジワジワとメモリが蝕まれ、最終的には致命的なメモリ不足エラー(OOM: Out of Memory)を引き起こします。
⚠️ メモリリークの仕組みと注意点
現代の多くの言語(JavaScriptやJava、Pythonなど)には、不要になったメモリを自動で掃除してくれる「ガベージコレクション(GC)」というメンテナンス機能が備わっています。しかし、GCがあるからといって安心はできません。
ガベージコレクションの盲点
GCは「どこからも参照されていないデータ」しか掃除できません。例えば、画面から消し去ったボタンのイベントリスナーがグローバルオブジェクトに残ったままだったり、タイマー処理(setInterval)をクリアし忘れたりしていると、GCは「まだ使うかもしれない」と勘違いしてメモリを解放してくれません。
// JavaScriptでのメモリリーク(イベントリスナーの解除漏れ)の例
function setupLeak() {
const hugeData = new Array(1000000).fill('🐱'); // 大容量のリソース
// グローバルなwindowオブジェクトにイベントを登録
window.addEventListener('resize', () => {
// hugeDataへの参照がここに残るため、関数が終了してもメモリから消えない
console.log(hugeData[0]);
});
}
// removeEventListenerで解除しない限り、関数を呼ぶたびにhugeDataがメモリに蓄積される 実務では、シングルページアプリケーション(SPA)のように「画面をリロードせずに長く開きっぱなしにするシステム」において、このフロントエンド側のメモリリークが頻発し、ブラウザの動作を重くする原因になります。
🛠️ メモリリークを賢く使うためのポイント
- ライフサイクルの徹底管理: コンポーネントが破棄されるタイミングで、必ずイベントリスナーの解除(removeEventListener)や、タイマーのクリア(clearInterval)を実行しましょう。
- プロファイリングツールの活用: ブラウザのデベロッパーツール(ChromeのMemoryタブなど)を使い、システムのタイムラインに沿ってメモリ使用量が右肩上がりに増え続けていないか定期的に計測しましょう。
- 弱参照(WeakMap / WeakSet)の利用: オブジェクトへの参照を弱く持つことで、そのオブジェクト自体が不要になった際に、マップ内のデータも連動して自動的にGCの対象にさせることができます。
いつもは広々と使える台も、茶トラが豪快にへそ天(解放漏れ)を決めているせいで、アメショが限界ギリギリまで追い詰められてしまいました。私たちのシステムも同様に、使い終わったリソースはこまめにクリーンアップして、いつでも快適に高負荷処理がこなせるクリアな環境を保ちたいですね。