コラム28. :メモリーリーク
C/C++言語で起きる厄介なバグ
メモリリーク(Memory Leak)とはコンピュータの動作中に、使用可能なメモリ容量がだんだん減っていく現象を指します。OSやアプリケーションソフトが処理のため確保したメモリ領域を、なんらかの理由で解放しないまま放置してしまうために起こります。
メモリリークを起こすと、システムの性能が低下したり、不安定になったりする。これを解消するには、最終的にシステムを再起動する必要があります。実はこのメモリリーク、C/C++言語でおこる、最も厄介なバグの一つです。
free、もしくはdeleteの処理忘れ
C言語およびC++言語でメモリリークが起こる理由は単純で、mallocおよび、newで生成したメモリを、freeおよび、deleteで開放しないことによって起こります。ほんの少しでしたらよいのですが、開放を忘れたメモリが少しずつ蓄積して他の領域を圧迫してしまうことにより、システムのスピードが遅くなるなどの現象が起こります。
例えば以下のようなプログラムで、簡単にメモリリークは起きます。
メモリリークを起こすプログラムのサンプル#include <stdio.h> #include <stdlib.h> #include <memory.h> int main(int argc, char** args){ int i; char *p; for (i = 0; i < 10000;i++){ // メモリを生成(開放しない) p = (char*)malloc(sizeof(char) * 10000); } return 0; }
mallocに対するfreeがないため、その分解放されないメモリが生成されたメモリ領域によって次第に圧迫されていきます。
「ならば、すべてfree、もしくはdeleteすればよいではないか」と思われるかもしれませんが、それがなかなかできないので、皆困っているわけです。そこで、こういった問題に対処するために様々な方法・ツールが発展してきました。
メモリリークを検出ツール
そういった問題に対処するために開発されたツールとして、メモリリーク検出ルールがあります。かつてはRational software社という会社が存在し、専門のソフトウェアとして販売されていましたが、現在この会社はIBMに買収されて無くなり、現在は同社のブランドとしてのみ存在しています。
また、現在、マイクロソフト社のVisualStuidio2015には、あらかじめ、「ネイティブ メモリ診断」機能として内蔵されています。こういったツールは大変便利なのですが、メモリリークの検出はできても、決定打にはなりません。結局は開発時にしっかりとした設計をするのが一番の対策です。
ガーベージコレクタ
また、別なアプローチとして、ガーベージコレクタ(garbage collector)を用いる手法があります。これは、プログラムが使用しなくなったメモリやプログラム間の隙間のメモリ領域を検出し解放する機能であるガベージコレクションを実行するためのモジュールのことで、
ガーベージコレクタは、メモリの生成をすれば、その消去はシステムは自動的に消去してくれるというもので、C#やJavaといった言語は、もともと組み込まれています。
ただ、C/C++言語にはもともと存在しておらず、使用するとなれば独自に作るか、ライブラリを使用しなくてはなりません。
現在はマルチスレッドを利用したプログラムが多いため、スレッド間でオブジェクトを共有して使用するといった処理はメモリ確保・解放の処理の記述が煩雑となりがちです。しかし、ガベージコレクタを持つ言語処理系では、この煩雑な記述を省略することができます。
ガーベージコレクタの問題点
とはいえ、ガーベージコレクタは完全とは言えません。ガベージコレクトが開始されると他の処理を止め、本処理が中断されるため、CPUを長時間(数百ミリ秒から数十秒)占有することもあります。その動作するタイミングやCPUの占有時間を予測することが困難なことから、リアルタイムシステムには使えないという問題点があります。
また、自分の好みのタイミングでメモリを解放できないのも難点です。「このタイミングでは、メモリを全て開放してできるだけ処理の負担を減らしたい」と思っても、思うようにはいかないのです。