2007年10月24日

メモリリークの自動検出と特定の方法

C/C++では他の言語ではあまりお目にかからない、メモリアロケーションを扱うことが出来る。便利な反面、使い方のお作法を間違えると利点以上の代償を払うことになる。メモリリークが発生する大きな要因としては、確保したメモリの開放のし忘れである。Javaなどとは違いガーベージコレクタは標準ではサポートされておらず、明示的な開放の記述が必要である。
Visual C++では、crtdbg.hというヘッダーにメモリリークを検出し、デバッグログに出力を行ってくれる独自の関数が定義されている。以下に使い方や応用例を示しておく。


まず、ヘッダーをロードしている記述のある場所を以下のように書き換えを行う。

#define _CRTDBG_MAP_ALLOC

//ここに任意の#includeを記述する
//例えばwindows.hやstdio.hなど

#ifdef _DEBUG
  #include <crtdbg.h>
  #define new  ::new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif

このような順番で記述を行う理由としては、まず下の方の#defineで既存のnewをオリジナルの記述へと置き換えを行っているが、これはcrtdbg.hのバグ対策用であり、new演算子を用いた場合に適切なファイル名がデバッグログに出力されない問題を修正するためのものであるが、たとえばこの記述を標準ライブラリより前に行ってしまうと、プリプロセス時に関係の無いnew演算子まで置き換えを行ってしまい、エラーを起こしてしまうからである。なお#defineで_CRTDBG_MAP_ALLOCの定義を一番先頭で行っているのには深い理由があるのだが、その解説は後日行う。

次にmain関数もしくはWinMain関数の先頭で以下の関数を呼び出す。

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
これは、プログラム終了と同時にVisual Studioのデバッグログにメモリリークの有無を出力することを指定する記述である。

以上の記述により、malloc(int);やnew int;などを行ったままでプログラムを終了すると、デバッグログにメモリリークの詳細が出力される。

ちなみに、もしGUIやDirectXを用いたソフトウェアを開発している場合に、AllocConsoleなどで確保したコンソールにメモリリークのログを出力したい場合は、WinMain関数の一番最後の行に以下の記述を行うことにより可能となる。

HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);  //標準出力のハンドルを取得
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);  //ログの出力対象をファイルハンドルに指定
_CrtSetReportFile(_CRT_WARN,hStdout);  //ワーニングの出力対象を第二引数のストリームに指定
_CrtDumpMemoryLeaks();  //実行時点での開放されていないメモリの一覧を表示


参考:
メモリ リークの検出と特定
_CRTDBG_MAP_ALLOC マクロは、正常に機能しません。
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。

この記事へのトラックバック