Visual Studio C++でメモリリークの検出 crtdbg.h CrtSetDbgFlagの使い方の紹介です。
Visual Studio C++でメモリリークの検出
Javaならいざ知らず、CやC++でプログラムをしていて誰も犯してしまうバグの一つがメモリリークだと思います。
Visual Studioにはメモリリークを検出するための便利な機能があります。
以下をプログラムの先頭に入れておきます。
1 2 3 |
#include “crtdbg.h” #define malloc(X) _malloc_dbg(X,_NORMAL_BLOCK,__FILE__,__LINE__) #define new ::new(_NORMAL_BLOCK, __FILE__, __LINE__) |
mainの先頭に以下のコードを入れておきます。
1 |
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_DELAY_FREE_MEM_DF|_CRTDBG_CHECK_ALWAYS_DF|_CRTDBG_LEAK_CHECK_DF); |
メモリリーク検出のサンプルプログラムは以下の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include "stdafx.h" #include "crtdbg.h" #define malloc(X) _malloc_dbg(X,_NORMAL_BLOCK,__FILE__,__LINE__) #define new ::new(_NORMAL_BLOCK, __FILE__, __LINE__) int _tmain(int argc, _TCHAR* argv[]) { _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_DELAY_FREE_MEM_DF|_CRTDBG_CHECK_ALWAYS_DF|_CRTDBG_LEAK_CHECK_DF); int *ptr; ptr = new int[8]; //delete ptr; return 0; } |
上記のプログラムを実行してみると「出力」のエリアでは以下の通り表示されます。
Detected memory leaks!
Dumping objects ->
C:\Users\xxxx\yyyy\memoryleak.cpp(11) : {120} normal block at 0x006CE2C8, 40 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.
プログラム ‘[2180] MemoryLeakCheck.exe’ はコード 0 (0x0) で終了しました。
delete ptr; のコメントを外して実行すると以下の通りになります。
プログラム ‘[5084] MemoryLeakCheck.exe’ はコード 0 (0x0) で終了しました。
このサンプルのようにメモリリークしている関数があるとソースと行を表示してくれるのでとても便利です。
ちなみに上記のプログラムで
delete ptr;
delete ptr;
のようにフリーしたポインターをさらにフリーしてみると以下のにエラーが表示されます。
ハンドルされない例外が0x57924F98(msvcr110d.dll)で発生しました
(MemoryLeakCheck.exe 内):0xC0000005: 場所 0x00008117 の読み取り中に
アクセス違反が発生しました。
このようなエラーを引き起こさないように、mallocとfree、newとdeleteは必ず対で使うように注意します。
デバッグモードでのメモリの内容
ところで、マイクロソフトのコンパイラ/OSによるデバッグモードでのメモリの内容は以下の通りです。
値 | 名前 | 内容 |
---|---|---|
0xCD | Clean Memory クリーンメモリ |
mallocやnewでメモリが確保されたけれどまだ使われていない状態。 |
0xDD | Dead Memory デッドメモリ |
deleteやfreeで解放されたメモリ。 |
0xED 0xBD |
Aligned Fence アラインフェンス |
アラインメモリ確保における「No man’s land」。 |
0xFD | Fence Memory フェンスメモリ |
確保されたメモリをラップするもの。「No man’s land」として知られる。つまり、プログラムではアクセスできない領域。 |
0xFD 0xFE |
Buffer slack バッファスラック |
メモリバッファにスラックスペースを入れるために使われる。 |
0xCC | /GZオプションでコンパイルされたときに、初期化されていない領域は自動的にこの値がセットされる。 | |
0xAB | Allocated Block アロケートブロック |
CランタイムではなくOSにより実行される。 LocalAlloc()により確保されるメモリ。 |
0xBAADF00B | Bad Food バッドフード |
CランタイムではなくOSにより実行される。 LMEM_FIXED付きのLocalAlloc()により確保されるメモリ。 |
0xFEEEFEEE | CランタイムではなくOSにより実行される。 ある目的のあるメモリだが、HeapAlloc()やLocalAlloc()では確保されていない、あるいはHeapFree()で解放されたメモリ。 |
これらのメモリの値を知っていると、デバッグ時にメモリがどのような状態にあるのかがわかって便利です。
メモリウィンドウの表示方法
Visual Studioでメモリウィンドウは以下のようにします。
Visual Studioのメニューから
[デバッグ] メニュー→ [ウィンドウ] →[メモリ] を押します。[メモリ 1]、[メモリ 2]、[メモリ 3]、または [メモリ 4] を押します。
各 [メモリ] ウィンドウは、任意の順序で開くことができます。
例えば上記のプログラムでは次のようになります。
メモリを確保した直後は、0xfdfdfdfdで囲まれた領域に0xcdで32バイトメモリが確保されていることがわかります。
次にdeleteが実行された直後は次の通りです。
先ほど0xcdで確保されていた場所が、すべて0xddに置き換わりました。
デバッグモードでこのようにヒープメモリが扱われていることを知っているとメモリリークなどの検出・デバッグに便利です。
まとめ:メモリリーク
メモリリークはプログラムの初心者から上級者まで常に作ってしまうバグとして注意しましょう。
プログラムにおいて単純に動作することと運用として使えることは異なります。
メモリリークバグのあるプログラムを一ヶ月ぐらい稼働していると見た目は安定して運用出来ていても、プログラムが徐々にOSのメモリを食いつぶして忘れたことにOSごとフリーズすることがあります。
メモリリークは厳密なチェックが望ましいですが、おおざっぱには、プログラム実行時に特定の操作を行う都度に、Windowsのタスクマネージャから見たメモリが増えていないかだけでもチェックしましょう。
プログラミングの無料レッスン体験
約8,000名の受講生と80社以上の導入実績のあるプログラミングやWebデザインのオンラインマンツーマンレッスンCodecamp
<Codecampの特徴>
1 現役エンジニアによる指導
2オンラインでのマンツーマン形式の講義
3大手企業にも導入されている実践的なカリキュラム
↓無料体験レッスン実施中です。
コメント