MFCでのメモリリークの検出方法

http://kkkon.hp.infoseek.co.jp/tips/MFC/MemoryLeak.shtml

何番目にメモリを割り当てたかという番号が毎回同じ時、_CrtSetBreakAlloc()を使うと便利。

追記:ページ消えちゃってたのでInternetArchiveから復刻

                                                                                                                              • -

MFCでのメモリリークの検出方法

MSDN HTMLヘルプ Version 4.74.8702 において、キーワードタブから「メモリ割り当ての追跡」にちょっとした情報は載っていますが、実際にどのように書き込んだり使ったりしたらいいのかの例が見当たりません。具体的には次のようにすれば検出できます。

#ifdef	__AFX_H__            // MFCのウィンドウを使う場合に限定しています
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#endif

以上のコードを、すべてのソースコード (*.cpp) のヘッダファイルをインクルードしたあとに追加します。


とりあえずこれを追加して実行して普通に終了させてみてください。もし、プログラムでメモリリークが起こっているのであれば、次のような出力がアウトプットウィンドウのデバッグの欄に表示されます。
(半角カタカナは全角カタカナに修正してあります)

スレッド 0x624 終了、終了コード 0 (0x0)。
Detected memory leaks!
Dumping objects ->
G:\Projects\DispBMPDoc.cpp(87) : {158} normal block at 0x014D0040, 921602 bytes long.
 Data: <                > 00 FF 00 00 FF 00 00 FF 00 00 FF 00 00 FF 00 00 
G:\Projects\DispBMPDoc.cpp(86) : {157} normal block at 0x00346E78, 40 bytes long.
 Data: <(               > 28 00 00 00 80 02 00 00 E0 01 00 00 01 00 18 00 
Object dump complete.
スレッド 0x620 終了、終了コード 0 (0x0)。
プログラム 'G:\Projects\Debug\DispBMP.exe' はコード 0 (0x0) で終了しました。

始めてみる場合は、何のことだかさっぱり分からないと思います。意味としては次のようになります。

Detected memory leaks!

メモリリークを検出しました。

Dumping objects ->

メモリリークしたブロックを表示していきます。

G:\Projects\DispBMPDoc.cpp(87) : {158} normal block at 0x014D0040, 921602 bytes long.

G:\Projects\DispBMPDoc.cppというファイルの87行目で割り当てられたブロックです。
プログラムの実行開始から158番目に割り当てられたブロックです。
割り当てられた番地は、0x014D0040番地です。(OSがWin9x、WinNTによっても値が変わってきます)
割り当てられたサイズは、921602 バイトです。

Data: < > 00 FF 00 00 FF 00 00 FF 00 00 FF 00 00 FF 00 00

割り当てられていたブロックに入っているデータの先頭部分が表示されます。

Object dump complete.

メモリリークしたブロックの表示が終わりました。


このような感じで表示されるので、とりあえずファイル名と行番号からメモリを割り当てている場所はわかります。
メモリ開放が行われていないはずなので、追加してください。

ちょっとしたコツ

何番目にメモリを割り当てたかという番号が毎回同じ時

プログラムを実行させて、終了させたときの {} の中の数字が毎回同じというときが有ると思います。
これは、ある条件の時にはちゃんと開放されるが、特定の条件のときに開放されていない場合が多いです。


割り当てるときの番号がある値になったときに、プログラムの実行をとめるとか言うこともできます。
さっきの場合は158番目に割り当てたメモリが、リークしているので158番目にメモリを割り当てる際に、
デバッガが停止するには以下のコードを追加してみてください。
(参考 MSDN キーワード 「デバッグ ルーチン」 _CrtSetBreakAlloc)

#include <crtdbg.h>

void main(void)
{
    _CrtSetBreakAlloc(158);

    ...
}

このコードを実行時の最初に追加すれば、158番目にメモリを割り当てる際にデバッガが停止するので、
どの条件のときにメモリリークが発生しているか突き止めやすいと思います。
その他の場合

マルチスレッドにしてプログラムした場合は、ちょっと再現性とかがなくなってくると思います。
あと、プログラムを実行させたときに行う操作とかをかえるだけで、結果が変わってきます。


どこで割り当てた分がメモリリークを起こしているかは、突き止めることができていると思います。
しかし、どの条件で発生しているかがはっきりとは分からないことが多いです。


そこで私が取った方法は、プログラムを実行させた際に行う処理を毎回同じにして、
メモリを割り当てる順番がある程度にかよってくるまで実行と終了を行います。


たとえば、155-173番目ぐらいに収束してきたら、150番目ぐらいでデバッガが止まるようにします。
そこから、トレースをかけて怪しい部分を重点的に調べました。


それで見つかるようならまだ良いですが、見つからない場合はソース全体をチェックする必要も出てきます。なるべくそうならないように、new や malloc 等のメモリ割り当て関数は注意して使う必要があります。


いまなら、STLのスマートポインタを使うとかすればJAVAのガベージコレクションみたいなことをやってくれるはずです。(私はまだ実際に使ったことがないのですが、Cマガにはそのように書いていました。)

                                                                                                                              • -

復刻おわり