ホーム < ゲームつくろー! < Programming TIPs編 < メモリリークの検出方法

その7 メモリリークの検出方法



 C言語で泣かされるのがメモリリークです。メモリリークはnew演算子などでヒープメモリを動的確保した後にdelete演算子などでそれを解放し忘れた時に発生します。プログラムが込み入ってきたり、複雑にヒープメモリを確保した場合など、うっかりdeleteし忘れる事があります。これはプログラムが複雑になってくるほど探すのが困難になります。

 Visual C++にはデバッグ時にメモリリークを自動検出する機能が備わっています。この章ではその方法を紹介します。



@ メモリリーク検出方法

 Visual C++のランタイムライブラリには「デバッグルーチン」と呼ばれるデバッグ専用の関数が沢山用意されています。その中でメモリリークを検出してくれるのは_CrtSetDbgFlag関数です。まず、この関数の宣言を見てみましょう:

_CrtSetDbgFlag関数
int_CrtSetDbgFlag(
   int newFlag
);

newFlagにはメモリリークの検出方法を表すフラグを指定します。

 newFlagに指定できるフラグについて簡単にまとめてみました。

フラグ 説明
_CRTDBG_ALLOC_MEM_DF デバッグ用のヒープメモリが使われるようになります。ランタイムはこのヒープメモリを検査することでメモリリークを検出しているわけです。このフラグは普通設定します。
_CRTDBG_DELAY_FREE_MEM_DF 解放し忘れの状態を意図的に作ります。これはメモリが少ない状態をシミュレートするのに効果的です。
_CRTDBG_CHECK_ALWAYS_DF ヒープメモリを割り当てた時と解放した時に_CrtCheckMemory関数を毎回呼び出します。_CrtCheckMemory関数は確保したヒープメモリの整合性をチェックする関数です。メモリのオーバーランなどで動的確保部分が侵食されてしまうなど、整合性が破壊されてしまった時のチェックに使われます。毎回呼び出すので実行速度が遅くなってしまいますが、整合性エラーをすばやく検出できます。
_CRTDBG_CHECK_CRT_DF Main関数(WinMain関数)が始まってからプログラムが完全に終わるまでのすべてのヒープメモリについてチェックが行われます。このフラグは普通設定しません。これを設定するとランタイムライブラリが確保したメモリについてもチェックが行われてしまい、大抵てんやわんやになります。
_CRTDBG_LEAK_CHECK_DF プログラムの終了時に_CrtDumpMemoryLeaks を呼び出して、メモリ リークのチェックを行いデバッグウィンドウに出力します。割り当てたメモリを解放し忘れている場合にはエラーレポートが出力されます。普通設定するフラグです。

 メモリリークだけを調べたい場合は_CRTDBG_ALLOC_MEM_DFと_CRTDBG_LEAK_CHECK_DFをフラグに設定します。



A 使い方

 この関数はデバッグ環境(_DEBUGが設定されている状態)でのみ動きます。Release環境では関数は全て取り除かれますので、動作に影響を与えません。この関数の使い方は極めて簡単です。例を挙げてみます:

#include "stdafx.h"
#include <crtdbg.h>


int _tmain(int argc, _TCHAR* argv[])
{
   // メモリリーク検出
   _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

   // ヒープメモリを確保
   int *pNumber = new int[3];
   pNumber[0] = 1;
   pNumber[1] = 2;
   pNumber[2] = 3;

   return 0;
}

 この関数はメイン関数の直後に設定するのが普通です。上のプログラムはpNumberにヒープメモリを動的に確保していますが、その解放を忘れています。この時、プログラム終了後にデバッグウィンドウには次のようなエラーレポートが出力されます。

エラーダンプ
Detected memory leaks!
Dumping objects ->
{95} normal block at 0x003E8520, 12 bytes long.
Data: < > 01 00 00 00 02 00 00 00 03 00 00 00
Object dump complete.

 0x003E8520に12バイトのメモリリークが発生した事を知らせてくれています。Dataの部分には12バイトの内容が示されていまして、プログラムで設定した数字が見て取れます。この情報を元にしてメモリリークが発生している箇所を割り出していきます。


 この関数を用いる事によって、プログラム上から殆どのメモリリークを取り除いていけます。エラーダンプが山ほど出てきた時にはこの関数を限定した範囲に絞る事により、少しずつ箇所が見えてきます。残念ながらCOMの解放し忘れをこの関数は検出してくれないのですが、十分過ぎる機能です。



B 謝辞

 この記事を書くきっかけを与えてくれましたスキン小僧さんに感謝申し上げます。