メモリ リークの検出と特定
MFC AppWizardなんかでアプリを作成すると、Debug動かしたときに、プログラム終了時に開放されていないメモリに対して警告を出してくれる。
Detected memory leaks! Dumping objects -> C:\work\Foo\Foo.cpp(240) : {53} normal block at 0x003F4548, 1 bytes long. Data: < > 00 Object dump complete. スレッド 0xD40 終了、終了コード 2 (0x2)。 プログラム 'C:\work\Foo\Debug\Foo.exe' はコード 2 (0x2) で終了しました。
この場合、Foo.cppの240行目に
LPTSTR szBuff = (LPTSTR) new TCHAR[nLen];
てな具合にnewで確保したメモリがあるわけだが、それが開放されていない、ってことを言ってくれている。newをコールするときに、__FILE__と__LINE__を記憶してくれているらしい。
ところで、先日AppWizardで作られたDLLを改造して、別のDLLを作成した。エクスポートする関数はすべて新規作成だったんだけど、わかりやすいようにそれらの関数は新規のcppファイルに記述して、その中に旧ソースをインクルードするという荒業をつかった。(旧ソースのstaticな変数や関数が必要だったの)
ある程度出来上がったところで動作確認を行ったら、古いDLLのソースをあちこち無理やりコメントアウトして作ったため、案の定"Detected memory leaks!"が表示された。どれどれ?とみてみたが、旧ソースのファイル名で、行数を見ると、その行はコメントだった。
????と思ってしばらく悩んでいたが、ふと、旧ソースの先頭を見るとこんな行が。
#ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif
ははーん、ここでnewを記憶するファイル名を取得してるわけ。新ソースは自分でCPPファイルを追加して作ったため、こんな行はない。一瞬、新ソースにもこの行を追加すればいいのかと思ったんだけど、いや待て、static変数じゃん、それじゃあだめだ。
たしか、"Detected memory leaks!"はcrtdbg.hだったような、と調べてみた。メモリ リークの検出と特定 などが参考に。どうやら_CRTDBG_MAP_ALLOCを定義すると、ファイル名と行番号を引数にとるnewが定義される様子。
void* PASCAL operator new(size_t nSize, LPCSTR lpszFileName, int nLine);
結局、StdAfx.hの末尾に、こんなのを付け加えてnewを再定義。旧ソースの例の部分はコメントアウトした。
// メモリリークを検出するため #ifdef _DEBUG #define _CRTDBG_MAP_ALLOC #define new ::new( _NORMAL_BLOCK, __FILE__, __LINE__ ) #endif // _DEBUG
ちなみに、通常のプロジェクトでも、こんな感じでメモリリーク検出が使用できる。適当なヘッダファイルを作って、以下を宣言する。
// ヘッダファイル #include <cstdlib> // 必要 #if _DEBUG #define _CRTDBG_MAP_ALLOC #include <crtdbg.h> #define new ::new( _NORMAL_BLOCK, __FILE__, __LINE__ ) #endif
そして、下の関数を必ず呼ばれる場所でコールしておくと、プログラム終了時に、開放されていないポインタについて、警告を出してくれます。
#if _DEBUG _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); #endif
プログラマーの友 第八報:メモリリークと crtdbg.hあたりが参考になるかも。てゆうか、crtdbg.hのお馬鹿なoperator newの話が笑える。
トラックバック
- このエントリーにトラックバック:
- http://frog.raindrop.jp/cgi-bin/mt/mt-tb.cgi/254
コメント