C++メモリリークおよび検出ツールの詳細

7486 ワード

まず、プログラムがメモリ漏洩しているかどうかを知ってから、どの行のコードがメモリ漏洩しているのかを特定して、それを修復する必要があります.
最も簡単な方法はもちろん専門の検査ツールを借りて、BoundsCheckのように有名で、機能は非常に強くて、C++の開発をする人はすべてそれを離れられないと信じています.また、ツールを使用せずに、メモリ漏洩の監視を自分で行う場合は、次の2つに分けられます.
一.MFCでメモリリークを検出
MFCのプログラムを使うなら簡単です.デフォルトではメモリリーク検出機能があります.
我々はVS 2005でMFCのダイアログボックスのプログラムを生成して、彼が自動的にメモリ漏れを検出できることを発見した.私たちが特別な操作をする必要はありません.よく見ると、CPPファイルごとに次のコードがあります.
 
  
#ifdef _DEBUG
#define new DEBUG_NEW
#endif

DEBUG_NEWというマクロはafxで定義する.hファイルでは、メモリの漏洩を特定するのに役立ちます.
以上のコードを含むcppファイルにメモリを割り当てて削除しなかった場合、プログラムを停止すると、VisualStudioのOutputウィンドウに次のようなメッセージが表示されます.
Detected memory leaks!Dumping objects ->d:\code\mfctest\mfctest.cpp(80) : {157} normal block at 0x003AF170, 4 bytes long. Data: < > 00 00 00 00 Object dump complete.
Outputウィンドウで太字の行をダブルクリックすると、IDEがファイルを開き、行にナビゲートし、メモリ漏洩が発生した場所がわかりやすくなります.
二.純C++のプログラムメモリ漏洩を検出する
VisualStudioで作成したWin 32 Console ApplicationとWin 32 Projectプロジェクトを試してみましたが、メモリリークは検出されませんでした.
次に,プログラムのメモリ漏洩検出のメカニズムを一歩一歩確立する.
まず、CライブラリのDebugバージョンが多くの検出機能を提供していることを知っておく必要があります.これにより、Debugプログラムをより容易にすることができます.MSDNでは専門の章でこれをDebug Routinesと言いますが、まず中身を見てみることをお勧めします.
私たちは中の重要ないくつかの関数を使います.その中で最も重要なのは_CrtDumpMemoryLeaks();MSDNの中の助けを自分で見ましょう.この関数を使用するには、ヘッダファイルcrtdbgを含める必要がある.h
この関数はDebugバージョンでのみ使用できます.デバッガの下でプログラムを実行すると、CrtDumpMemoryLeaksは「Output(出力)」ウィンドウにメモリリーク情報を表示する.セグメントコードを書いて試してみましょう.以下のようにします.
メモリ漏洩バージョン1の検出:
 
  
#include "stdafx.h"
#include
int _tmain(int argc, _TCHAR* argv[])
{
    int* p = new int();
    _CrtDumpMemoryLeaks();
    return 0;
}

実行後、Output(出力)ウィンドウに次の情報が表示されます.
Detected memory leaks!Dumping objects ->{112} normal block at 0x003AA770, 4 bytes long. Data: 00 00 00 00 Object dump complete.
しかし、これはプログラムにメモリが漏れていることを教えてくれただけで、いったいどこで漏れているのか一目では見えません.
メモリ漏洩の検出バージョン2を見てください.
 
  
#include "stdafx.h"
#ifdef _DEBUG
#define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_CLIENTBLOCK
#endif
#define _CRTDBG_MAP_ALLOC
#include
#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
#endif
int _tmain(int argc, _TCHAR* argv[])
{
    int* p = new int();
    _CrtDumpMemoryLeaks();
    return 0;
}

このプログラムはいくつかのマクロを定義、Debugバージョンのnewをマクロによって置き換え、新しいnewはnewを呼び出す際のファイル名とコード行を記録する.実行すると、次の結果が表示されます.
Detected memory leaks!Dumping objects ->d:\code\consoletest\consoletest.cpp(21) : {112} client block at 0x003A38B0, subtype 0, 4 bytes long. Data: 00 00 00 00 Object dump complete.
ふふ、もうMFCプログラムの効果と同じですが、ちょっと待ってください.次のコードを見てみましょう.
 
  
int _tmain(int argc, _TCHAR* argv[])
{
    int* p = new int();
    _CrtDumpMemoryLeaks();
    delete p;
    return 0;
}

実行すると、ポインタが削除されていることがわかりますが、メモリの漏洩が報告されています.したがって、newを呼び出すたびに、プログラム内でその呼び出しが記録されることは想像できます.配列レコードのようなものです.deleteがあれば、それを配列から削除します.CrtDumpMemoryLeaks()は,この配列の現在の状態を印刷する.
だから必要なときにDumpからメモリ情報が出る以外に、最も重要なのはプログラムが終了する時に一度削除する必要があることです.CrtDumpMemoryLeaks();
プログラムに出口が1つ以上ある場合は、複数の場所で関数を呼び出す必要があります.
さらに,プログラムがクラスの構造関数からポインタを削除したらどうする.例:
 
  
#include "stdafx.h"
#ifdef _DEBUG
#define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_CLIENTBLOCK
#endif
#define _CRTDBG_MAP_ALLOC
#include
#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
#endif
class Test
{
public:
    Test()      {   _p = new int();     }
    ~Test()     {   delete _p;          }
    int* _p;
};
int _tmain(int argc, _TCHAR* argv[])
{
    int* p = new int();
    delete p;
    Test t;
    _CrtDumpMemoryLeaks();
    return 0;
}

解析関数がプログラム終了時に呼び出され,メモリ漏洩はないのに,このような書き方が報告されていることがわかる.
どのように改善するかは、メモリの漏洩バージョン3を検出することです.
 
  
#include "stdafx.h"
#ifdef _DEBUG
#define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_CLIENTBLOCK
#endif
#define _CRTDBG_MAP_ALLOC
#include
#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
#endif
class Test
{
public:
    Test()      {   _p = new int();     }
    ~Test()     {   delete _p;          }
    int* _p;
};
int _tmain(int argc, _TCHAR* argv[])
{
    _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
    int* p = new int();
    delete p;
    Test t;
    return 0;
}

 _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );この文は、プログラム終了時に自動的に呼び出されます.CrtDumpMemoryLeaks.同時に_を設定する必要がありますCRTDBG_ALLOC_MEM_DFと_CRTDBG_LEAK_CHECK_DF.
このように、このバージョンはMFCのような効果に達していますが、これだけでは足りないと思います.Outputウィンドウに情報を出力するだけなので、開発者への注意はまだ明らかではなく、よく漏れてしまいます.また、メモリの漏れが発見されても、修復が難しく、プログラムの外在表現に深刻な影響を及ぼすことはなく、修復しません.どのようにして開発者にメモリ漏洩の問題を自発的に修復させることができますか?かつて人と協力してプログラムを書いたことを覚えていて、私の関数のパラメータは要求があって、空にすることができなくて、しかし他の人はいつも空の値を伝えて、仕方がなくて、関数で関数のパラメータを検証し始めて、彼にassertに住んで、このようにプログラムの運行の時いつも絶えずassertを弾いて、プログラムのあの煩わしさをデバッグして、最後に他のプログラマーは煩わして、この問題を直しました.パラメータを入力すると正しいです.だから私たちはプログラマーに自分から一つのことをさせなければならないと思います.まず、このことをするのは自分の負担を軽減し、自分の仕事を楽にすることができると思っています.ほほほ、私达もこのようにして、プログラムが退出する时、メモリの漏洩を検出してプログラムにヒントを与えます.
メモリの漏洩を検出するバージョン4を参照してください.
 
  
#include "stdafx.h"
#include
#ifdef _DEBUG
#define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_CLIENTBLOCK
#endif
#define _CRTDBG_MAP_ALLOC
#include
#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
#endif
void Exit()
{
    int i = _CrtDumpMemoryLeaks();
    assert( i == 0);
}
int _tmain(int argc, _TCHAR* argv[])
{
    atexit(Exit);
    int* p = new int();
    return 0;
}

このバージョンでは、プログラムの終了時にメモリの漏洩を確認し、存在する場合はプロンプトダイアログボックスをポップアップする.
atexit(Exit);プログラム終了時に実行するExit()関数が設定されています.Exit()関数でメモリリークがある場合、CrtDumpMemoryLeaks()は0以外の値を返し、assertに泊まります.
このバージョンが使えるようになった.しかし、コード内のすべてのメモリの漏洩を正確に検出するには、コード内の#define......をnewを使用するすべてのファイルにコピーする必要があるため、改善することもできます.すべてのファイルがこんなに多くのコードをコピーすることはできません.だから、私は彼を抽出して、KdetectMemoryLeakのようなファイルに置くことができます.hにおいて、このファイルの内容は以下の通りである.
 
  
#pragma once
#ifdef _DEBUG
#define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_CLIENTBLOCK
#endif
#define _CRTDBG_MAP_ALLOC
#include
#include
#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
#endif

そしてKdetectMemoryLeak.hはプロジェクトの共通ファイルに含む、例えばVSで構築されたプロジェクトはstdafxに含まれる.h中.あるいは自分で作ったCommonですhファイルには、一般的な、基本的にすべてのファイルで使用されるコードが含まれています.