私はWindowsプラットフォームのC++プログラマーです。 Visual Studio 2008を使用しています。
私は通常、メモリリークのあるコードになります。
通常、コードを調べることでメモリリークを見つけますが、面倒で常に良いアプローチとは限りません。
有料のメモリリーク検出ツールを購入する余裕がないので、メモリリークを回避するための最善の方法を提案してもらいたいと思いました。
説明
必要なもの
オペレーターの基本を理解します。 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.
削除した場合にのみメモリを再割り当てします。以下のコードでは、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.
これらのポインターの割り当てを確認してください。すべての動的変数(ヒープに割り当てられたメモリ)は、ポインターに関連付ける必要があります。動的変数がそのポインターから関連付け解除されると、消去できなくなります。繰り返しますが、これはメモリリークを引き起こします。
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!
ローカルポインターには注意してください。関数で宣言したポインターはスタックに割り当てられますが、それが指す動的変数はヒープに割り当てられます。削除しない場合、プログラムが関数を終了した後も保持されます。
void Leak(int x){
char* p = new char [x];
// delete [] p; // Remove the first comment marking to correct.
}
「削除」後の角括弧に注意してください。単独で「delete」を使用して、単一のオブジェクトを解放します。ヒープ配列を解放するには、角括弧で「削除」[]を使用します。このようなことをしないでください:
char* one = new char;
delete [] one; // Wrong
char* many = new char [30];
delete many; // Wrong!
リークがまだ許可されている場合-私は通常、deleakerでそれを探しています(ここで確認してください: http://deleaker.com )。
ありがとう!
コードでいくつかの手法を使用して、メモリリークを検出できます。最も一般的で最も簡単な検出方法は、マクロ、たとえば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関数によるメモリリークをキャッチすることはできません。ただし、完全なメモリデバッガーソリューションと比較すると、非常に使いやすく、非常に高速です。
直接メモリリークが発生するリスクを最小限に抑えるのに役立つ、よく知られたプログラミング手法がいくつかあります。
new
およびdelete
を常にペアで記述し、割り当て/割り当て解除コードがペアで呼び出されることを確認してくださいvector<T> t
の代わりに可能な限りT* t = new T[size]
を使用しますgflags
ユーティリティを使用して、ユーザーモードのスタックトレースを有効にします。UMDH
を使用して、プログラムのメモリのスナップショットを複数作成します。メモリが割り当てられる前にスナップショットを撮り、プログラムがメモリをリークしたと思われる時点の後、2番目のスナップショットを撮ります。プログラムに一時停止またはプロンプトを追加して、UMDH
を実行してスナップショットを取る機会を与えることができます。UMDH
を再度実行します。今回は、2つのスナップショット間の差分を行うモードで実行します。次に、メモリリークの疑いのあるコールスタックを含むレポートを生成します。gflags
設定を復元します。UMDH
は、プロセス全体のメモリ割り当てを監視しているため、CRTデバッグヒープよりも多くの情報を提供します。サードパーティのコンポーネントがリークしているかどうかもわかります。
「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」呼び出しがあることを確認してください。
Gccを使用する場合、gprofが利用可能です。
プログラマがどのようにメモリリークを見つけるかを知りたい
いくつかはツールを使用し、いくつかはあなたがすることを行い、ピアコードレビューを通してもできます
プログラムにメモリリークがないことを確認するために従うべき標準または手順はありますか
私にとって:動的に割り当てられたオブジェクトを作成するたびに、常に解放コードを後に配置し、その間にコードを埋めます。間にコードに例外がないことが確実な場合、これは問題ありません。それ以外の場合は、try-finallyを使用します(C++は頻繁に使用しません)。
new
の出現をコードで検索し、それらがすべてデストラクターで一致する削除を伴うコンストラクター内で発生することを確認します。これがそのコンストラクターで唯一のスロー操作であることを確認してください。これを行う簡単な方法は、すべてのポインターをstd::auto_ptr
またはboost::scoped_ptr
にラップすることです(移動セマンティクスが必要かどうかによって異なります)。今後のすべてのコードでは、デストラクタ内のリソースをクリーンアップするオブジェクトがすべてのリソースを所有していることを確認してください。移動セマンティクスが必要な場合は、r-value参照をサポートするコンパイラーにアップグレードして(VS2010はそう信じています)、移動コンストラクターを作成できます。それをしたくない場合は、スワップの良心的な使用を伴うさまざまなトリッキーなテクニックを使用するか、Boost.Moveライブラリを試してください。
Visual Studioには、Cランタイムライブラリと呼ばれるメモリリークの検出器が組み込まれています。メイン関数が戻った後にプログラムが終了すると、CRTはアプリケーションのデバッグヒープをチェックします。デバッグヒープにまだブロックが割り当てられている場合、メモリリークが発生します。
このフォーラム は、C/C++でのメモリリークを回避するいくつかの方法について説明しています。
Visual Leak Detector(VLD) は、Visual C++用の無料の堅牢なオープンソースのメモリリーク検出システムです。
Visual Studioデバッガーでプログラムを実行すると、Visual Leak Detectorはデバッグセッションの最後にメモリリークレポートを出力します。リークレポートには、full call stackが含まれ、リークしたメモリブロックがどのように割り当てられたかが示されます。コールスタック内の行をダブルクリックして、エディターウィンドウ内のそのファイルと行にジャンプします。
クラッシュダンプしかない場合は、Windbg !heap -l
コマンドを使用して、リークしたヒープブロックを検出できます。 gflagsオプション「ユーザーモードスタックトレースデータベースの作成」を開くと、メモリ割り当て呼び出しスタックが表示されます。
Valgrindツールを使用して、メモリリークを検出できます。
また、特定の関数のリークを見つけるには、関数の最後にexit(0)を使用してからValgrindで実行します
`$` valgrind ./your_CPP_program
AddressSanitizer (ASan)は高速メモリエラー検出器です。 C/C++プログラムのuse-after-freeおよび{heap、stack、global} -buffer overflowバグを検出します。それは見つけます:
このツールは非常に高速です。インストルメント済みプログラムの平均的なスローダウンは、約2倍です。
質問の2番目の部分に答えると、
プログラムにメモリリークがないことを確認するために従う必要のある標準または手順はありますか。
はいあります。これは、CとC++の主な違いの1つです。
C++では、ユーザーコードでnew
またはdelete
を呼び出さないでください。 RAII は非常に一般的に使用される手法であり、リソース管理の問題をほとんど解決します。プログラム内のすべてのリソース(リソースは取得する必要があり、後でリリースする必要があるものです:ファイルハンドル、ネットワークソケット、データベース接続だけでなく、プレーンメモリ割り当て、場合によってはAPI呼び出しのペア(BeginX( )/ EndX()、LockY()、UnlockY())は、クラスでラップする必要があります。ここで、
new
を呼び出して)その後、このクラスは、new
を呼び出してポインターを保存することにより、ローカルで、スタック上で、またはクラスメンバーとして、およびnotインスタンス化されます。
多くの場合、これらのクラスを自分で定義する必要はありません。標準ライブラリコンテナもこのように動作するため、std::vector
に格納されているオブジェクトは、ベクターが破棄されると解放されます。繰り返しになりますが、new
とdelete
を呼び出すにはyouが必要ですが、オブジェクトitself(メモリ管理for free)同様に、スマートポインタークラスを使用して、new
で割り当てる必要があるオブジェクトを簡単にラップし、その有効期間を制御できます。
これは、オブジェクトがスコープから外れると、自動的に破棄され、リソースが解放されクリーンアップされることを意味します。
コード全体で一貫してこれを行うと、メモリリークが発生しなくなります。 couldがリークするものはすべて、オブジェクトが宣言されたスコープを制御が離れたときに呼び出されることが保証されているデストラクタに関連付けられています。
MTuner は、MSVC、GCC、およびClangコンパイラをサポートする無料のマルチプラットフォームメモリプロファイリング、リーク検出、分析ツールです。機能が含まれます:
ユーザーは、GCCまたはClangクロスコンパイラを使用して、プラットフォームをターゲットとするソフトウェアのプロファイルを作成できます。 MTunerには、Windows、PlayStation 4およびPlayStation 3プラットフォームのサポートが組み込まれています。
Windowsでは、 CRTデバッグヒープ を使用できます。
プログラムにメモリリークがないことを確認するために従う必要のある標準または手順はありますか。
手動のメモリ管理を使用しないでください(delete
またはdelete[]
を手動で呼び出した場合、間違っています)。 RAIIとスマートポインターを使用して、ヒープの割り当てを絶対最小値に制限します(ほとんどの場合、自動変数で十分です)。
他の回答で提供されているツールとメソッドに加えて、静的コード分析ツールを使用して、メモリリーク(およびその他の問題)も検出できます。無料の堅牢なツールはCppcheckです。しかし、他にも多くのツールが利用可能です。 Wikipedia には、静的コード分析ツールのリストがあります。
アプリケーションコードで「新規」または「削除」のいずれも使用しないでください。代わりに、マネージャー/ワーカーのイディオムを使用する新しいタイプを作成します。マネージャー/ワーカーイディオムでは、マネージャークラスがメモリを割り当てて解放し、他のすべての操作をワーカーオブジェクトに転送します。
残念ながら、C++には「演算子」のオーバーロードがないため、これは本来よりも多くの作業です。ポリモーフィズムが存在する場合、さらに多くの作業が必要になります。
しかし、メモリリークについて心配する必要がないため、努力する価値があります。つまり、メモリリークを探す必要さえありません。