web-dev-qa-db-ja.com

C ++コード/プロジェクトでメモリリークを見つける方法は?

私はWindowsプラットフォームのC++プログラマーです。 Visual Studio 2008を使用しています。

私は通常、メモリリークのあるコードになります。

通常、コードを調べることでメモリリークを見つけますが、面倒で常に良いアプローチとは限りません。

有料のメモリリーク検出ツールを購入する余裕がないので、メモリリークを回避するための最善の方法を提案してもらいたいと思いました。

  1. プログラマがメモリリークを見つける方法を知りたい。
  2. プログラムにメモリリークがないことを確認するために従う必要のある標準または手順はありますか?
152
Chris_vr

説明

必要なもの

  • C++の習熟度
  • C++コンパイラ
  • デバッガーおよびその他の調査用ソフトウェアツール

1

オペレーターの基本を理解します。 C++演算子「new」は、ヒープメモリを割り当てます。 「削除」演算子は、ヒープメモリを解放します。すべての「新規」に対して、「削除」を使用して、割り当てた同じメモリを解放する必要があります。

char* str = new char [30]; // Allocate 30 bytes to house a string.

delete [] str; // Clear those 30 bytes and make str point nowhere.

2

削除した場合にのみメモリを再割り当てします。以下のコードでは、strは2番目の割り当てで新しいアドレスを取得します。最初のアドレスは回復不能に失われ、それが指していた30バイトも失われます。今ではそれらを解放することは不可能であり、メモリリークが発生します。

char* str = new char [30]; // Give str a memory address.

// delete [] str; // Remove the first comment marking in this line to correct.

str = new char [60]; /* Give str another memory address with
                                                    the first one gone forever.*/

delete [] str; // This deletes the 60 bytes, not just the first 30.

3

これらのポインターの割り当てを確認してください。すべての動的変数(ヒープに割り当てられたメモリ)は、ポインターに関連付ける必要があります。動的変数がそのポインターから関連付け解除されると、消去できなくなります。繰り返しますが、これはメモリリークを引き起こします。

char* str1 = new char [30];

char* str2 = new char [40];

strcpy(str1, "Memory leak");

str2 = str1; // Bad! Now the 40 bytes are impossible to free.

delete [] str2; // This deletes the 30 bytes.

delete [] str1; // Possible access violation. What a disaster!

4

ローカルポインターには注意してください。関数で宣言したポインターはスタックに割り当てられますが、それが指す動的変数はヒープに割り当てられます。削除しない場合、プログラムが関数を終了した後も保持されます。

void Leak(int x){

char* p = new char [x];

// delete [] p; // Remove the first comment marking to correct.

}

5

「削除」後の角括弧に注意してください。単独で「delete」を使用して、単一のオブジェクトを解放します。ヒープ配列を解放するには、角括弧で「削除」[]を使用します。このようなことをしないでください:

char* one = new char;

delete [] one; // Wrong

char* many = new char [30];

delete many; // Wrong!

6

リークがまだ許可されている場合-私は通常、deleakerでそれを探しています(ここで確認してください: http://deleaker.com )。

ありがとう!

227
John Smith

コードでいくつかの手法を使用して、メモリリークを検出できます。最も一般的で最も簡単な検出方法は、マクロ、たとえばDEBUG_NEWを定義し、それを__FILE____LINE__などの事前定義マクロとともに使用して、コード内のメモリリークを見つけることです。これらの定義済みマクロは、メモリリークのファイルと行番号を示します。

DEBUG_NEWは、通常は次のように定義されているマクロです。

#define DEBUG_NEW new(__FILE__, __LINE__)
#define new DEBUG_NEW

したがって、newを使用する場所はどこでも、プログラムでメモリリークを見つけるために使用できるファイルと行番号を追跡できます。

そして、__FILE____LINE__定義済みマクロ で、使用する場所でそれぞれファイル名と行番号を評価します!

DEBUG_NEWを他の興味深いマクロと非常に美しく使用する方法を説明する次の記事を読んでください。

クロスプラットフォームメモリリークディテクタ


Wikpedia から

Debug_newは、メモリ割り当ておよび割り当て解除の呼び出しをインターセプトするために、演算子newおよび演算子deleteをオーバーロードおよび/または再定義して、メモリ使用量のプログラムをデバッグするC++の手法を指します。 多くの場合、DEBUG_NEWという名前のマクロを定義し、newをnew(_FILE_、_LINE_)割り当てに関するファイル/行情報を記録します。Microsoft Visual C++は、Microsoft Foundation Classesでこの手法を使用します。一部のプラットフォームでファイル/行情報を表示できる一方で、マクロの再定義の使用を回避するために、このメソッドを拡張する方法がいくつかあります。この方法には多くの固有の制限があります。 C++にのみ適用され、mallocなどのC関数によるメモリリークをキャッチすることはできません。ただし、完全なメモリデバッガーソリューションと比較すると、非常に使いやすく、非常に高速です。

28
Nawaz

直接メモリリークが発生するリスクを最小限に抑えるのに役立つ、よく知られたプログラミング手法がいくつかあります。

  • 独自の動的メモリ割り当てを行う必要がある場合は、newおよびdeleteを常にペアで記述し、割り当て/割り当て解除コードがペアで呼び出されることを確認してください
  • 可能であれば、動的なメモリ割り当てを避けてください。たとえば、vector<T> tの代わりに可能な限りT* t = new T[size]を使用します
  • ブーストスマートポインターのような「スマートポインター」を使用する( http://www.boost.org/doc/libs/1_46_1/libs/smart_ptr/smart_ptr.htm
  • 私のお気に入り:ポインターの所有権の概念を理解していることを確認し、ポインターを使用するすべての場所で、どのコードエンティティが所有者であるかを確認してください
  • c ++コンパイラーによって自動的に作成されるコンストラクター/代入演算子と、ポインターを所有するクラスがある場合の意味(または、オブジェクトへのポインターを含むクラスがある場合の意味notown)。
14
Doc Brown
  1. ダウンロード Windows用デバッグツール
  2. gflagsユーティリティを使用して、ユーザーモードのスタックトレースを有効にします。
  3. UMDH を使用して、プログラムのメモリのスナップショットを複数作成します。メモリが割り当てられる前にスナップショットを撮り、プログラムがメモリをリークしたと思われる時点の後、2番目のスナップショットを撮ります。プログラムに一時停止またはプロンプトを追加して、UMDHを実行してスナップショットを取る機会を与えることができます。
  4. UMDHを再度実行します。今回は、2つのスナップショット間の差分を行うモードで実行します。次に、メモリリークの疑いのあるコールスタックを含むレポートを生成します。
  5. 完了したら、以前のgflags設定を復元します。

UMDHは、プロセス全体のメモリ割り当てを監視しているため、CRTデバッグヒープよりも多くの情報を提供します。サードパーティのコンポーネントがリークしているかどうかもわかります。

8
Aaron Klotz
8
CantGetANick

「Valgrind」の実行:

1)メモリリークの特定に役立つ-発生したメモリリークの数を表示し、リークされたメモリが割り当てられたコード内の行を指摘します。

2)メモリを解放しようとする間違った試みを指摘する(例:「delete」の不適切な呼び出し)

「Valgrind」の使用手順

1)valgrindを取得 here

1)-gフラグを使用してコードをコンパイルします

3)シェルで実行:

valgrind --leak-check=yes myprog arg1 arg2

ここで、「myprog」はコンパイル済みプログラム、「arg1」、「arg2」はプログラムの引数です。

4)結果は、malloc/newの呼び出しのリストであり、その後にfree deleteの呼び出しがありませんでした。

例:

==4230==    at 0x1B977DD0: malloc (vg_replace_malloc.c:136)

==4230==    by 0x804990F: main (example.c:6)

(解放されていない)mallocが呼び出された行を示します。

他の人が指摘したように、すべての「new」/「malloc」呼び出しに対して、後続の「delete」/「free」呼び出しがあることを確認してください。

7
Gal Nachmana

Gccを使用する場合、gprofが利用可能です。

プログラマがどのようにメモリリークを見つけるかを知りたい

いくつかはツールを使用し、いくつかはあなたがすることを行い、ピアコードレビューを通してもできます

プログラムにメモリリークがないことを確認するために従うべき標準または手順はありますか

私にとって:動的に割り当てられたオブジェクトを作成するたびに、常に解放コードを後に配置し、その間にコードを埋めます。間にコードに例外がないことが確実な場合、これは問題ありません。それ以外の場合は、try-finallyを使用します(C++は頻繁に使用しません)。

5
LeleDumbo

newの出現をコードで検索し、それらがすべてデストラクターで一致する削除を伴うコンストラクター内で発生することを確認します。これがそのコンストラクターで唯一のスロー操作であることを確認してください。これを行う簡単な方法は、すべてのポインターをstd::auto_ptrまたはboost::scoped_ptrにラップすることです(移動セマンティクスが必要かどうかによって異なります)。今後のすべてのコードでは、デストラクタ内のリソースをクリーンアップするオブジェクトがすべてのリソースを所有していることを確認してください。移動セマンティクスが必要な場合は、r-value参照をサポートするコンパイラーにアップグレードして(VS2010はそう信じています)、移動コンストラクターを作成できます。それをしたくない場合は、スワップの良心的な使用を伴うさまざまなトリッキーなテクニックを使用するか、Boost.Moveライブラリを試してください。

5
Mankarse
  1. Visual Studioには、Cランタイムライブラリと呼ばれるメモリリークの検出器が組み込まれています。メイン関数が戻った後にプログラムが終了すると、CRTはアプリケーションのデバッグヒープをチェックします。デバッグヒープにまだブロックが割り当てられている場合、メモリリークが発生します。

  2. このフォーラム は、C/C++でのメモリリークを回避するいくつかの方法について説明しています。

5
Benny Tjia

Visual Leak Detector(VLD) は、Visual C++用の無料の堅牢なオープンソースのメモリリーク検出システムです。

Visual Studioデバッガーでプログラムを実行すると、Visual Leak Detectorはデバッグセッションの最後にメモリリークレポートを出力します。リークレポートには、full call stackが含まれ、リークしたメモリブロックがどのように割り当てられたかが示されます。コールスタック内の行をダブルクリックして、エディターウィンドウ内のそのファイルと行にジャンプします。

クラッシュダンプしかない場合は、Windbg !heap -lコマンドを使用して、リークしたヒープブロックを検出できます。 gflagsオプション「ユーザーモードスタックトレースデータベースの作成」を開くと、メモリ割り当て呼び出しスタックが表示されます。

4
fresky

Valgrindツールを使用して、メモリリークを検出できます。

また、特定の関数のリークを見つけるには、関数の最後にexit(0)を使用してからValgrindで実行します

`$` valgrind ./your_CPP_program 
3
Divyanshu

AddressSanitizer (ASan)は高速メモリエラー検出器です。 C/C++プログラムのuse-after-freeおよび{heap、stack、global} -buffer overflowバグを検出します。それは見つけます:

  • 解放後の使用(ダングリングポインター逆参照)
  • ヒープバッファオーバーフロー
  • スタックバッファオーバーフロー
  • グローバルバッファオーバーフロー
  • 返却後に使用
  • 初期化順序のバグ

このツールは非常に高速です。インストルメント済みプログラムの平均的なスローダウンは、約2倍です。

3
Beginner

質問の2番目の部分に答えると、

プログラムにメモリリークがないことを確認するために従う必要のある標準または手順はありますか。

はいあります。これは、CとC++の主な違いの1つです。

C++では、ユーザーコードでnewまたはdeleteを呼び出さないでください。 RAII は非常に一般的に使用される手法であり、リソース管理の問題をほとんど解決します。プログラム内のすべてのリソース(リソースは取得する必要があり、後でリリースする必要があるものです:ファイルハンドル、ネットワークソケット、データベース接続だけでなく、プレーンメモリ割り当て、場合によってはAPI呼び出しのペア(BeginX( )/ EndX()、LockY()、UnlockY())は、クラスでラップする必要があります。ここで、

  • コンストラクターacquiresリソース(リソースがmemroy割り当ての場合はnewを呼び出して)
  • デストラクタはリソースを解放します
  • コピーと割り当ては、コピーコンストラクタと割り当て演算子をプライベートにすることによって防止されるか、正しく機能するように実装されます(たとえば、基になるリソースのクローンを作成することによって)

その後、このクラスは、newを呼び出してポインターを保存することにより、ローカルで、スタック上で、またはクラスメンバーとして、およびnotインスタンス化されます。

多くの場合、これらのクラスを自分で定義する必要はありません。標準ライブラリコンテナもこのように動作するため、std::vectorに格納されているオブジェクトは、ベクターが破棄されると解放されます。繰り返しになりますが、newdeleteを呼び出すにはyouが必要ですが、オブジェクトitself(メモリ管理for free)同様に、スマートポインタークラスを使用して、newで割り当てる必要があるオブジェクトを簡単にラップし、その有効期間を制御できます。

これは、オブジェクトがスコープから外れると、自動的に破棄され、リソースが解放されクリーンアップされることを意味します。

コード全体で一貫してこれを行うと、メモリリークが発生しなくなります。 couldがリークするものはすべて、オブジェクトが宣言されたスコープを制御が離れたときに呼び出されることが保証されているデストラクタに関連付けられています。

3
jalf

MTuner は、MSVC、GCC、およびClangコンパイラをサポートする無料のマルチプラットフォームメモリプロファイリング、リーク検出、分析ツールです。機能が含まれます:

  • タイムラインベースのメモリ使用量とライブメモリブロックの履歴
  • ヒープ、メモリタグ、時間範囲などに基づく強力なメモリ操作フィルタリング.
  • 完全なソースコードを使用した手動計測用SDK
  • コマンドラインの使用による継続的な統合サポート
  • スタックツリーとツリーマップナビゲーションを呼び出す
  • はるかに。

ユーザーは、GCCまたはClangクロスコンパイラを使用して、プラットフォームをターゲットとするソフトウェアのプロファイルを作成できます。 MTunerには、Windows、PlayStation 4およびPlayStation 3プラットフォームのサポートが組み込まれています。

3
mtosic

Windowsでは、 CRTデバッグヒープ を使用できます。

プログラムにメモリリークがないことを確認するために従う必要のある標準または手順はありますか。

手動のメモリ管理を使用しないでください(deleteまたはdelete[]を手動で呼び出した場合、間違っています)。 RAIIとスマートポインターを使用して、ヒープの割り当てを絶対最小値に制限します(ほとんどの場合、自動変数で十分です)。

3
Cat Plus Plus

他の回答で提供されているツールとメソッドに加えて、静的コード分析ツールを使用して、メモリリーク(およびその他の問題)も検出できます。無料の堅牢なツールはCppcheckです。しかし、他にも多くのツールが利用可能です。 Wikipedia には、静的コード分析ツールのリストがあります。

0
orbitcowboy

アプリケーションコードで「新規」または「削除」のいずれも使用しないでください。代わりに、マネージャー/ワーカーのイディオムを使用する新しいタイプを作成します。マネージャー/ワーカーイディオムでは、マネージャークラスがメモリを割り当てて解放し、他のすべての操作をワーカーオブジェクトに転送します。

残念ながら、C++には「演算子」のオーバーロードがないため、これは本来よりも多くの作業です。ポリモーフィズムが存在する場合、さらに多くの作業が必要になります。

しかし、メモリリークについて心配する必要がないため、努力する価値があります。つまり、メモリリークを探す必要さえありません。

0
s. heller