8

Windows で C++ プログラムのメモリ リークを検出したいと考えています。Mermoy リーク検出に関する MSDN のドキュメントも読み、Visual Leak Detector も使い始めました。

リークの報道に疑問を持っています。行番号付きのファイル名を期待していますが、常に以下のテキストが報告されます。ファイル名と行番号を除いて、リーク記述のすべてのコンポーネント (ブロックタイプ、メモリアドレス、データなど) が含まれています。

本当のリークなら?はいの場合、ファイル/行が報告されない理由を知っていますか? その間、私はこのURLも見ています

ありがとう

メモリ リークを検出しました。
オブジェクトのダンプ ->
{4723} 0x04AFB5B8 の通常ブロック、長さ 8 バイト。
 データ: 2C 3F 00 00 28 3F 00 00
{1476} 0x04AC3B58 の通常ブロック、長さ 12 バイト。
 データ: 00 CD CD CD EB 01 75 4C CA 3D 0B 00
オブジェクトのダンプが完了しました。
4

7 に答える 7

7

メモリリークを追跡するさまざまな方法を調査しました。それらにはすべて長所がありますが、短所もあります。

それらの長所と短所を理解するには、さまざまなメカニズムと要件を理解する必要があります。

  1. new、delete、malloc、freeはどのようにインターセプトされますか?一部のツールは#defineを使用してnew、delete、malloc、freeを再定義しますが、これはインクルードファイルの正しい順序に依存し、クラスにfreeというメソッドが含まれている場合(Qtの場合のように)に問題が発生する可能性があります。プリプロセッサもこのメソッドを再定義します。これにより、コンパイルエラーや未解決の外部が発生する可能性があります。

    もう1つの方法は、グローバルなnew演算子とdelete演算子を無効にすることです。これははるかにクリーンなソリューションですが、失敗するのは、ライブラリに新しいライブラリを配置するサードパーティのライブラリがあり、ヘッダーに削除がある(またはその逆)場合です。

  2. 通話の発信元はどのように決定されますか。new、delete、...が#defineを使用してインターセプトされた場合、多くの場合プリプロセッサシンボル__FILE____LINE__あり、リークの原因を取得するために使用されます。ただし、CreateString()などのコードに「ジェネリック」関数がある場合、リークのほとんどはこれらのジェネリック関数で報告されるため、実際には役に立ちません。

    別の方法は、実行時にコールスタックを取得することです。これは、WindowsのStackWalk関数を使用して非常に簡単に実行できますが、私の経験では、これは非常に低速です。はるかに高速な代替手段は、ベースポインターを直接取得し、スタックフレームポインターに依存することです(スタックフレームポインターを取得するには、/ Oy-を使用してコンパイルする必要があります)。フレーム(ベース)ポインタは次のように取得できます:_asm mov DWORD PTR [FramePtr], ebp。次に、単にループし、ループ内で命令ポインタをから取得し((ADDR *)FramePtr)[1];、次のフレームポインタをから取得しますFramePtr = ((ADDR *)FramePtr)[0];

  3. 正確な瞬間にリークを報告する方法。私の場合、アプリケーションの最後にリークを報告したいのですが、これを行うには、アプリケーションの最後にリーク報告メカニズムが必要です。つまり、リークを自分で報告する場合は、アプリケーションの最後に破棄されるグローバル変数に依存する必要があります(そして、グローバル変数のデストラクタでリークを報告します)。サーバータイプのアプリケーションの場合、おそらく2つの時点でのメモリ使用量の違いを取得することに関心があります。

そして今、さまざまなリークシステム:

  1. Cランタイム:最後にリークを報告しますが、コールスタックを報告する適切な方法はありません。new、delete、...への呼び出しをインターセプトするその方法は、サードパーティのライブラリ(Qt、Boostなど)との組み合わせで問題を引き起こす可能性があります

  2. 外部のMicrosoftユーティリティ(GFlags、UMDH 、、 ...など):2つの時点の違いのみをログに記録できるようです。ただし、GFlagsユーティリティがOSにフラグを設定すると、アプリケーションの深刻な速度低下を引き起こす可能性がありますが、コールスタックの方がはるかに優れているようです。

  3. ビジュアルリークディテクタ。すべてのリークを正しく検出しているようですが、私の場合、DllUnloadでプロセスを中止するサードパーティのDLLがあるため、機能しません(Windows 7固有の問題のようです)。

  4. 私の個人的なお気に入り(そして人々は私に同意しないでしょう)は、あなた自身のメモリマネージャーを書くことです。インターセプトは、グローバルなnew演算子とdelete演算子を使用して簡単に実行でき(上記の問題が発生する可能性があります)、上記のようにコールスタックを取得できます。この代替手段は、アプリケーションの最後の瞬間に実行されるコードを使用できることにも依存しています。

代替案を選択するとき、私は次の側面が私の状況にとって非常に重要であることに気づきました。

  • リークが発生した場合にすべての開発者にすぐに通知されるように、アプリケーションで問題なく動作するようにしたいと思います。Purifyなどの外部ユーティリティを使用する後でリークチェックを遅らせると、リーク検出がはるかに困難になります。
  • アプリケーションの最後にリークを自動的に報告したい。
  • リークから可能な限り多くの情報(データ、コールスタックなど)が必要です

お役に立てれば。

于 2010-08-25T11:26:36.597 に答える
5

これは、Visual Leak Detector からの出力ではなく、Visual Studio 独自のデバッグ CRT からの出力です。まず、 Codeplexで現在のバージョンを使用していることと、プロジェクトに #include vld.h があることを確認してください。より有益な出力が得られます。

于 2010-08-25T10:34:58.770 に答える
2

多くのヘッダーファイルをデバッグした後に取得しました。

出力でファイル/行番号を有効にするために必要なことは次のとおりです

#define _CRTDBG_MAP_ALLOC
#define _CRTDBG_MAP_ALLOC_NEW
于 2010-08-25T13:24:37.520 に答える
1

デバッグ情報を有効にしてコンパイルし、pdbファイルがリークディテクタで利用可能であることを確認しましたか?この情報がないと、行番号を提供できません。

于 2010-08-25T13:31:47.673 に答える
0

Valgrindを使用する必要があります。これは非常に強力で、プログラムのどこにリークがあるかを適切に説明します。ただし、プログラムはgccでコンパイルする必要があるかもしれません...

于 2010-08-25T09:57:42.120 に答える
0

割り当て番号 (中括弧内の番号) が常に同じである場合、これが役立つ可能性があります。基本的には、VC++で指定数の割り当てを試みた際にブレークポイントを発生させる方法を説明しました。

于 2010-08-25T10:58:42.067 に答える
0

Rational Purifyは VC++ の有料プラグインとして利用でき、非常に優れたリーク (およびその他のトラブル) 検出器です。以前はSolarisでよく使っていましたが、とても使いやすくクリアでした。また、Visual Studio で使用するバージョンについて他の人から良いことを聞いたことがありますが、実際に試したことはありません。

FWIW、すでに言及されている Valgrind のインスピレーションは Purify にあったのではないかと思います。

于 2010-08-25T10:44:41.290 に答える