メモリリーク

メモリリーク (Memory leak) とは、プログラミングにおけるバグの一種。プログラムが確保したメモリの一部、または全部を解放するのを忘れ、確保したままになってしまうことを言う。プログラマによる単純なミスやプログラムの論理的欠陥によって発生することが多い。 近年のマルチプロセスOSではメモリ空間がプロセス（プログラム）ごとに独立に確保され、プロセス終了とともに解放される。したがって、メモリリークが単独で発生する場合、あるいはそのプロセスがすぐに終了される場合は、深刻な影響をもたらすことはあまりないと言ってよい。しかし、メモリリークが繰り返し起きて大量にメモリが消費された場合、他のプログラムやOSが確保可能なメモリが少なくなり、以下の2つの現象が発生する。 OSやプログラムがメモリを確保する際にRAMからのみ確保する場合、エラーを引き起こす、または即座に停止する。 OSやプログラムが直接RAMの領域を確保するのではなく、仮想メモリを使用している場合（最近のOSはこのタイプ）、プログラムのメモリ使用量（ワーキングセット）が一定値に達するとページングが多く発生するようになる。最終的に、仮想メモリを使い果たすと、メモリ確保のAPI（典型的にはmalloc関数）から戻らなくなって停止するか、nullが戻るがメモリ確保の失敗に対し、予防的に書かれていない場合はセグメンテーション違反を起こすなど、何らかの、多くは異常終了による、プロセス終了となる。 メモリリークが以下のような状況で起こった場合、問題は特に深刻になる。 プログラムが長期間動き続けるとき。サーバーサイドアプリケーションや組み込みシステムは年単位で稼働し続けることもある。 共有メモリのような、確保したまま終了することが許されるメモリ領域をプログラムが使っているとき。 ゲームや動画を扱うプログラムのように、メモリの確保・解放を頻繁に行うとき。 OSやシステムそのものがメモリリークを起こすとき。 組み込みシステムやポータブル機のように、メモリの絶対量が少ないとき。 AmigaOSのように、プロセスが終了してもメモリが自動的に解放されないOSを利用しているとき。メモリリークから回復する手段は再起動しかない。 メモリリークを診断するためには、一般的にはプログラムの論理構造を調べる、デバッガを使って内部状態を確認するなどの手段をとる必要がある。時系列に沿ってメモリ消費量をトレースすることによりヒントが得られることもある。キャッシュを使うプログラムの場合、設定を間違えていれば消費メモリのサイズはシステムを停止させるまで際限なく増え続けてゆくかもしれない。メモリリークが発生していることが分かっている場合、最も単純な原因調査の方法は、しばらくシステムを放置した後にメモリ上の適当なオブジェクトを拾うことである。同じ種類のオブジェクトが大量に見つかれば、おそらくその発生源がリーク元である。メモリのダンプを取得して解析することも診断手段である。Windows用にはマイクロソフトから無料のツールが用意されている。 しばしば、メモリを大量に消費する症状のみを見て、プログラムがメモリリークを起こしていると誤診断されることがある。しかし、メモリの消費量のみからメモリリークか否かを判断するのは難しい。なぜならば、仮にメモリが（不必要に思われるほど）大量に消費されていたとしても、そのプログラムが本当にそれだけのメモリを必要としていたり、将来必要になるという理由で確保したりしている可能性があるからである。単にメモリを無駄遣いしているだけということもありうる（プログラムのバグではあってもメモリリークではない）。 また、メモリを解放しているはずなのにプログラムの使用メモリが減らない現象を見て、メモリリークが疑われる場合がある。実は、ほとんどのプログラムはメモリの確保・解放処理をライブラリ経由（C言語のmalloc関数など）で行っている。プログラムが解放したメモリは、再度の確保の為にライブラリが蓄えておく（プールしておく）ため、OSから見えるメモリの使用量が減らないように見えているのである。 Windows上では、Intel Parallel InspectorやMicro Focus BoundsCheckerといったサードパーティー製のメモリエラー検出ツールに、メモリリーク検出機能が付属しているので、これらを利用してメモリリーク位置を特定することが可能である。ただし誤検出することもあり、完全ではないので、前記の診断方法と併用することが望ましい。メモリデバッガも参照。 プログラミング言語によってはメモリ解放をコンピューター（仮想マシン、フレームワークなどのシステム側）に委ねるガベージコレクションが取り入れられており、メモリリークが起きにくくなっている。ガベージコレクションを言語組込みの機能として持つ言語には、LispやJava、C#（VB.NETなどの.NET言語はすべて該当）、Objective-C（バージョン2.0以降）、PHP、Pythonなどがある。 一方でCのようなガベージコレクションが組み込まれていないプログラミング言語では、メモリリークが起きないように注意深くプログラムを設計する必要がある。C++においてはガベージコレクションこそないものの、デストラクタや所有権などの機能・概念 (RAII) により、ほぼ同等の機構が実現できる。これは後述のリソースリークにも適用できる。 ガベージコレクションはどこからも参照されていないメモリ領域を自動的に解放する。しかし、プログラマの意図・把握しないところに参照が残っていると、今後全く利用されないはずのメモリ（オブジェクト）がガベージコレクションによる解放の対象にならず、確保されたままになることがある。これもまたメモリリークの一種であり、最終的にはメモリ不足によりシステムをクラッシュさせる等、上記のような問題と全く同種の問題を引き起こす。この種類のメモリリークに関してガベージコレクションは無力である（なお、これはガベージコレクションの無いシステムでは、メモリ管理的には解放したにもかかわらず、そのメモリ領域を指すポインタが残っている、というようなコードに相当する。そういったポインタの参照は相当に運が良くない限り一般にセグメンテーション違反にはならず、たいていは後から確保され書き込まれた別のデータを読み書きすることになり、デバッグどころか発見すら極度に困難なバグとなるが、それでもガベージコレクションよりはマシである、と信じる向きもある）。 リソースリーク (Resource leak) とは、メモリリークをファイルやネットワーク接続、OS、ハードウェアなど一般のリソースに拡張した概念である。 例えば、一般的なOS環境でファイルへの読み書きをする場合、最初にファイルを開く処理が必要であり、これはメモリでいえば確保する処理にあたる。ファイルに対する処理が終了した時には、ファイルを閉じて変更内容を反映させる処理が必要であり、これはメモリを解放する処理にあたる。このとき、閉じる処理を忘れて、不要なファイルがいつまでも開いた状態にあれば、リソースリークが発生したことになる。ユーザーが他のプログラムでそのファイルを開こうとすると、原因不明のエラーに悩まされることになる。ほとんどの場合、リソースリークを起こしたプログラムが終了すれば、OSによって正常な状態に復帰される。 Windowsでは標準のタスク マネージャーやProcess Explorerといったツールを使って、各プロセスが使用しているハンドルの数を監視することで、リソースリークを診断することが可能である。 主記憶装置 メモリ管理 ヒープ領域 ガベージコレクション リソース メモリダンプ プロセスリーク メモリデバッガ 負荷ツール ^ Chen, Raymond 『Windowsプログラミングの極意 歴史から学ぶ実践的Windowsプログラミング!』 アスキー、2007年（原著2006年12月）。ISBN 978-4756150004。