6

私は現在、大規模なアプリケーション プロジェクト (C++ で記述) に取り組んでいます。このプロジェクトは、少し前にゼロから開始されたものであり、メモリ リークのチェックのまとめを行うことが必須になった時点に達しました。

このアプリケーションは Ubuntu Linux で実行され、多くのマルチメディア コンテンツがあり、3D グラフのレンダリング、ウィンドウ、オーディオ、ムービーの再生など、さまざまな目的で OpenGl、SDL、および ffmpeg を使用します。それはビデオゲームではありませんが、ビデオゲームと考えることができますが、アプリケーションの義務はビデオゲームと見なすことで単純化できます。

現在、メモリリークがまだあるかどうかを判断するのに少し無知です。過去に、すでに一部を特定して削除していました。しかし最近では、アプリケーションはほぼ完成しており、私たちが実行したテストでは正確に把握できない結果が得られています。

私が最初にしたことは、Valgrind を介してアプリケーションを実行しようとすることでした...残念ながら、valgrind 環境で実行するとアプリケーションがクラッシュします。さまざまな場所でクラッシュするため、「非決定論的」にクラッシュします。そこで、潜在的なリークの原因を簡単に特定するために Valgrind を使用することをあきらめ、free と top の 2 つの Linux コマンドを使用することになりました。

free は、アプリケーションの実行中にシステム メモリの使用状況を調査するために使用されています。

top は「-p」オプションとともに使用され、実行中のアプリケーション プロセスのメモリ使用量を調べます。

出力フォーム top and free は、後処理のためにファイルにダンプされています。質問の下部にリンクされているデータを使用して 2 つのグラフを作成しました。

テスト ケースは非常に単純です。アプリケーションが既に起動され、コマンドを待機しているときに、メモリに関するデータがプローブされます。次に、常に同じことを繰り返し実行する一連のコマンドを開始します。アプリケーションは、大量のマルチメディア データを RAM にロードしてからダウンロードすることが期待されています。

残念ながら、グラフは私が期待していたものを示していません。メモリ使用量は 3 つの異なる段階を経て増加し、その後停止します。メモリは明らかに決して解放されていません。これは、巨大なメモリ リークがあったことを示唆しています。これはまったく問題ありません。これは、メディアによって消費されたメモリを解放していない可能性が非常に高いことを意味するためです。

しかし、最初の 3 つのステップの後...メモリ使用量は安定しています...これ以上大きなステップはありません...予想されるデータのロードとアンロードに対応するわずかな上下があります。ここで予期しないのは、ロード/アンロードされるはずのデータが RAM の 100 分の 1 のメガバイトを占めていることです。

私は現在、これらのデータを解釈する上でかなり無知です。

誰にもいくつかのヒントや提案がありますか? 私は何が欠けていますか?巨視的なメモリリークの存在を確認するために使用している方法は完全に間違っていますか? メモリ リークをチェックするための Valgrind 以外の (できれば無料の) ツールを知っていますか?

システムメモリ使用量グラフ

プロセスのメモリ使用量グラフ

4

7 に答える 7

5

Valgrind をあきらめる代わりに、Valgrind と協力して、

  • Valgrind で遭遇したバグを取り除く
  • 更新された Valgrind を使用して、アプリを徹底的にテストおよびデバッグしてください。

問題の解決策である Valgrind をあきらめたと言っても、実際には役に立ちません...

Valgrind は、Linux でメモリ リークやスレッドの問題をチェックするために使用するツールです。

最終的には、別の解決策を探すよりも、「Valgrind が自分のアプリで動作しない理由」を理解することに時間を費やす方が間違いなく優れています。Valgrind は実証済みでテスト済みのツールですが、完全ではありません。そして、それはロングショットで代替方法を打ち負かします.

Valgrind のページでは、バグは Bugzilla に提出したほうがよいと書かれていますが、実際にはhttps://lists.sourceforge.net/lists/listinfo/valgrind-usersで質問したほうがよいでしょう。状況。最悪のシナリオ - bugzilla にバグを報告するか、自分でバグを報告するように指示されます。

于 2012-12-29T17:34:37.763 に答える
3

初めに...

そして、メモリ リークのチェックのまとめを行うことが必須の時点に達しました。

これは、実は方法論の問題です。正確さは、後付けではなく、ソフトウェアの主要な目標であるべきです。

これで、コミットごとにインストルメント化された単体テストを実行していたら、問題を特定するのがどれほど簡単だったかがわかったと思います。


それで、今何をしますか?

  • ランタイム検出:

    • Valgrind を機能させてみてください。おそらく環境問題がいくつかあります
    • ASanThreadSan、およびMemSanを試してください。Linuxでのセットアップは簡単ではありませんが、とても印象的です!
    • インストルメント化されたビルドを試してください: tcmallocには、たとえばヒープチェッカーが含まれています
    • ...
  • コンパイル時の検出:

    • 警告をオンにします (できれば-Werror) (問題に固有のものではありません)
    • Clangなどの静的分析を使用すると、ペアになっていない割り当てルーチンが見つかる場合があります
    • ...
  • 人検知:

    • コード レビュー: すべてのリソースが RAII クラス内に割り当てられていることを確認する
    • ...

注: RAII クラスのみを使用すると、メモリ リークの除去には役立ちますが、ダングリング リファレンスには役立ちません。ありがたいことに、ダングリング参照の検出は ASan の機能です。


そして、すべての問題にパッチを適用したら、これがプロセスの一部になることを確認してください。腐った卵がコードベースを汚すのではなく、すぐに淘汰されるように、変更は常にレビューおよびテストする必要があります。

于 2012-12-29T19:34:57.750 に答える
2

freeからの結果はtop役に立ちません。結果のグラフを作成するのに苦労したことを残念に思います。ここの同様のトピックで、なぜそれらが役に立たないのかをよく説明しました: Linux での C++ アプリケーションのメモリ安定性

Valgrindで発生しているクラッシュのトラブルシューティングを優先する必要があるという他の回答にも同意します。この時点で Valgrind は非常に安定していると考えられており、個人的にかなり複雑なマルチスレッド マルチメディア SDL/OpenGL などを実行しています。問題なくそれを介してアプリ。Valgrind の実行環境が、アプリケーションの不安定性を明らかにしている可能性が非常に高いです。クラッシュはスレッド競合状態のクラッシュのように聞こえますが、ヒープ/メモリの破損の可能性もあります。

その場合、Valgrind の実行環境内からクラッシュしているアプリをデバッグする方法についてのアドバイスが必要になる場合があります (これに対する答えはわかりません)。

于 2012-12-29T18:10:17.627 に答える
2

free と top の問題点は、問題を示すことはできますが、問題の修正にはほとんど役に立たないことです。メモリを割り当てる数百または数千のコード行のうち、リークしているのはどれですか? ここで valgrind が役立ちます。

これがツールの予算がある会社の場合は、purify またはその他の商用ツールを検討することができます。

完全を期すために、Boehm の保守的なガベージ コレクション メモリ アロケータ (C および C++ コードで動作) について言及します。GC をオフにして GC_Free() を使用すると、リーク検出ツールになります。または、GC を有効のままにして、使用されなくなったときにメモリを自動的に解放することもできます。

于 2012-12-29T18:15:52.703 に答える
2

おそらくvalgrindを見たいと思うでしょう。

そして、非常に単純な例から始めて、valgrind が何をレポートするかを感じ取ってください。valgrind が正確に何がどれだけ不足しているかを示す単純化された例を考えてみましょう:

edd@max:/tmp$ cat valgrindex.cpp 

#include <cstdlib>

int main() {
  double *a = new double[100];
  exit(0);
}
edd@max:/tmp$ g++ -o valgrindex valgrindex.cpp 
edd@max:/tmp$ valgrind ./valgrindex
==15910== Memcheck, a memory error detector
==15910== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==15910== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==15910== Command: ./valgrindex
==15910== 
==15910== 
==15910== HEAP SUMMARY:
==15910==     in use at exit: 800 bytes in 1 blocks
==15910==   total heap usage: 1 allocs, 0 frees, 800 bytes allocated
==15910== 
==15910== LEAK SUMMARY:
==15910==    definitely lost: 0 bytes in 0 blocks
==15910==    indirectly lost: 0 bytes in 0 blocks
==15910==      possibly lost: 0 bytes in 0 blocks
==15910==    still reachable: 800 bytes in 1 blocks
==15910==         suppressed: 0 bytes in 0 blocks
==15910== Rerun with --leak-check=full to see details of leaked memory
==15910== 
==15910== For counts of detected and suppressed errors, rerun with: -v
==15910== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
edd@max:/tmp$ 
于 2012-12-29T17:28:36.933 に答える
0

それはすべて、使用しているアロケーターによって異なります。libc アロケーター (malloc、calloc、realloc) と C++ アロケーター (new、delete) は、メモリを OS に解放しないという最適化トリックを使用している可能性があります。おわかりのように、malloc にメモリを要求し、それを使用してから解放すると、必ずしも OS に解放されるとは限りません。むしろ、malloc にメモリを要求すると、(ほとんどのユリ) 必要以上に取得されます (ページ境界のため)。そうすれば、次回さらにメモリが必要になったときに、malloc がそれを保持します。無料でも同じこと。メモリはおそらく mallocs メモリ プールに追加されたばかりで、そこから後で割り当てが行われます。

そのため、アプリケーションの最初の数回の malloc でメモリが非常に高くなりますが、プールは将来の割り当てに対応するのに十分な大きさになります。

于 2012-12-29T17:41:01.253 に答える
0

使用に加えて、 Boehm の保守的な GC のvalgrind使用を検討することもできます。おそらくそれをコンパイルして、メモリリーク検出器として構成したいと思うでしょう。

また、あえて Boehm の GC をプライマリ メモリ アロケータとして使用することもできます。

ところで、調べてみると/proc/1234/maps役立ちます(1234はプロセスのpidです)。

于 2012-12-29T20:03:36.150 に答える