C++ でのメモリ リークの検出

巴扎黑
リリース: 2016-11-30 11:24:50
オリジナル
1187 人が閲覧しました

まず、プログラムにメモリ リークがあるかどうかを知り、次にメモリ リークのあるコード行を見つけて修正する必要があります。

もちろん、最も簡単な方法は、有名で非常に強力な BoundsCheck などの専門的な検出ツールを使用することです。C++ を開発する人はこれなしではいられないでしょう。また、その必要はありません。まず、プログラムにメモリ リークがあるかどうかを確認し、次にメモリ リークのあるコード行を特定して修復する必要があります。

もちろん、最も簡単な方法は、有名で非常に強力な BoundsCheck などの専門的な検出ツールを使用することです。C++ を開発する人はこれなしではいられないでしょう。また、ツールを使用せずにメモリ リークの監視を自分で実装します。これは次の 2 つの状況に分けられます:

1. MFC でのメモリ リークの検出

MFC プログラムを使用している場合は、とてもシンプルです。デフォルトでメモリリーク検出機能があります。

MFC ダイアログ ボックス プログラムを生成するために VS2005 を使用したところ、特別な操作を行う必要がなく、メモリ リークを自動的に検出できることがわかりました。注意深く観察した結果、各 CPP ファイルに次の記述があることがわかりました。コード:

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

DEBUG_NEW このマクロは afx.h ファイルで定義されており、メモリ リークの特定に役立ちます。

上記のコードを含む cpp ファイルにメモリを割り当てた後にメモリが削除されなかった場合、プログラムが停止すると、VisualStudio の出力ウィンドウに次の情報が表示されます:

メモリ リークが検出されました!
オブジェクトのダンプ ->
d:codemfctestmfctest .cpp(80) : {157} 通常ブロック、0x003AF170、長さ 4 バイト。
データ: < 00 00 > オブジェクト ダンプが完了しました。

出力ウィンドウの太線をダブルクリックします。その後、IDE はファイルを開いてこの行を見つけます。メモリ リークが発生した場所は簡単にわかります。

2.純粋な C++ プログラムでのメモリ リークの検出

Visual Studio で作成された Win32 コンソール アプリケーションと Win32 プロジェクトのプロジェクトを試しましたが、結果はメモリ リークを検出できませんでした。

以下は、プログラムのメモリ リーク検出メカニズムを確立するための段階的なプロセスです。

まず、C ランタイム ライブラリのデバッグ バージョンには、プログラムのデバッグを容易にする多くの検出関数が提供されていることを知っておく必要があります。 MSDN には、「デバッグ ルーチン」と呼ばれるこれに関する特別な章があるので、最初にその内容を読むことをお勧めします。

その中でいくつかの重要な関数を使用します。最も重要なものは _CrtDumpMemoryLeaks です。MSDN のヘルプをご確認ください。この関数を使用するには、ヘッダー ファイル crtdbg.h をインクルードする必要があります

この関数はデバッグ バージョンでのみ役立ちます。デバッガーでプログラムを実行すると、_CrtDumpMemoryLeaks が [出力] ウィンドウにメモリ リーク情報を表示します。コードの一部 次のように試してください:

メモリ リーク バージョン 1 の検出:

#include "stdafx.h"

#include
int _tmain(int argc, _TCHAR* argv[])
{
int* p = new int;
_CrtDumpMemoryLeaks;
return 0;
}

実行後、次の情報が出力ウィンドウに表示されます:

メモリ リークが検出されました!

オブジェクトのダンプ ->
{112} 通常ブロック0x003AA770 、長さは 4 バイトです。
データ: < 00 00 >
オブジェクト ダンプが完了しました。

しかし、これはプログラムにメモリ リークがあることを示しているだけで、それがどこにあるかを判断するのは困難です。

メモリリーク検出バージョン 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;
0を返す;
}

このプログラムは、デバッグ バージョンの new がマクロに置き換えられ、実行後、次の結果が表示されます。リーク!

オブジェクトのダンプ ->

d:codeconsoletestconsoletest.cpp(21) : 0x003A38B0 の {112} クライアント ブロック、サブタイプ 0、長さ 4 バイト。

データ: < 00 > オブジェクトのダンプが完了しました。

はは、MFC プログラムと同じ効果がありますが、ちょっと待ってください。次のコードを見てください:

int _tmain(int argc, _TCHAR* argv[])
{
int* p = new int;

_CrtDumpMemoryLeaks;

delete p;

return 0;

}

ポインタを削除しましたが、それでもメモリ リークが報告されました。したがって、 new が呼び出されるたびに、配列レコードがある場合と同様に、呼び出しがプログラムの内部で記録され、削除されると配列から削除され、_CrtDumpMemoryLeaks が現在のステータスを出力することが想像できます。配列の 。

そのため、必要に応じてメモリ情報をダンプすることに加えて、最も重要なことは、プログラムの終了時に _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 < crtdbg.h>
#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 & lt; CRTDBG.H & GT;
#Ifdef _debug
#define New Debug_ClientBlock
#Endifehclass テスト
{
Public: PTest {_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 と同じ効果が得られましたが、出力ウィンドウに情報が出力されるだけであり、開発者へのリマインダーだけなので、これでは不十分だと思います。明らかではなく、見落とされることが多く、多くの人がメモリ リークを見つけたとしても修復するのは簡単ではなく、プログラムの外部パフォーマンスに深刻な影響を与えるものではないため、修復しようとしません。開発者はメモリ リークを積極的に修正するにはどうすればよいでしょうか?以前、誰かと共同でプログラムを書いたことがあるのですが、関数のパラメータには要件があり、空にすることはできませんでしたが、他の関数では常に null 値が渡され、関数の先頭で関数のパラメータを検証してアサートするしかありませんでした。このようにして、プログラムの実行中に常にアサートが表示され、プログラムのデバッグが非常にストレスになりました。最終的に、他のプログラマーがイライラして、問題を修正し、パラメーターを正しく入力しました。なので、プログラマに率先して何かをやってもらいたいのであれば、まず「こうすれば自分の負担が減って仕事が楽になる」と感じてもらう必要があると思います。はは、プログラムが終了するときにメモリ リークが検出された場合は、同じことを実行しましょう。 Memory Leak検出バージョン4:

##stdafx.h "を含める

#contlide&lt; assert.h&gt; CLIENTBLOCK

#endif

#define _CRTDBG_MAP_ALLOC

#include
#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
#endif
void Exit
{
int i = _CrtDumpMemoryLeaks;
asse rt( i == 0);
}
int _tmain(int argc, _TCHAR* argv[])
{
atexit(Exit);
int* p = new int;
return 0;
}

このバージョンでは、プログラムの終了時にメモリ リークがチェックされます。存在する場合、プロンプトダイアログボックスが表示されます。

atexit(Exit); プログラムの終了時に実行される Exit 関数を設定します。 Exit 関数では、メモリ リークがある場合、_CrtDumpMemoryLeaks が 0 以外の値を返し、アサートされます。

このバージョンはすぐに使用できます。ただし、コード内のすべてのメモリ リークを本当に正確に検出したい場合は、コード内の #define... を new を使用してすべてのファイルにコピーする必要があるため、まだいくつかの改善は可能です。ファイルごとにそれほど多くのコードをコピーすることは不可能なので、それを抽出してファイルに配置します。たとえば、ファイルの内容は次のとおりです。 #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 ファイルを自分で作成しました。このファイルには、基本的にすべてのファイルで使用される共通のコードが含まれています。



関連ラベル:
c
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート