Valgrindによって検出されたまだ到達可能なリーク


154

このブロックで言及されている関数はすべてライブラリ関数です。このメモリリークを修正するにはどうすればよいですか?

まだ到達可能」カテゴリの下にリストされています。(さらに4つあります。非常によく似ていますが、サイズはさまざまです)

 630 bytes in 1 blocks are still reachable in loss record 5 of 5
    at 0x4004F1B: calloc (vg_replace_malloc.c:418)
    by 0x931CD2: _dl_new_object (dl-object.c:52)
    by 0x92DD36: _dl_map_object_from_fd (dl-load.c:972)
    by 0x92EFB6: _dl_map_object (dl-load.c:2251)
    by 0x939F1B: dl_open_worker (dl-open.c:255)
    by 0x935965: _dl_catch_error (dl-error.c:178)
    by 0x9399C5: _dl_open (dl-open.c:584)
    by 0xA64E31: do_dlopen (dl-libc.c:86)
    by 0x935965: _dl_catch_error (dl-error.c:178)
    by 0xA64FF4: __libc_dlopen_mode (dl-libc.c:47)
    by 0xAE6086: pthread_cancel_init (unwind-forcedunwind.c:53)
    by 0xAE61FC: _Unwind_ForcedUnwind (unwind-forcedunwind.c:126)

キャッチ:プログラムを実行すると、メモリリークは発生しませんでしたが、Valgrind出力に1行追加されましたが、これは以前には存在しませんでした。

munmap()により、/ lib / libgcc_s-4.4.4-20100630.so.1の0x5296fa0-0x52af438でsymsを破棄する

リークを修正できない場合、munmap()行によってValgrindが0の「まだ到達可能な」リークを報告する理由を誰かが少なくとも説明できますか?

編集:

これが最小限のテストサンプルです。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

void *runner(void *param) {
    /* some operations ... */
    pthread_exit(NULL);
}

int n;

int main(void) {

    int i;
    pthread_t *threadIdArray;

    n=10; /* for example */

    threadIdArray = malloc((n+n-1)*sizeof(pthread_t));  

    for(i=0;i<(n+n-1);i++) {
        if( pthread_create(&threadIdArray[i],NULL,runner,NULL) != 0 ) {
            printf("Couldn't create thread %d\n",i);
            exit(1);
        }
    }


    for(i=0;i<(n+n-1);i++) {
        pthread_join(threadIdArray[i],NULL);
    }

    free(threadIdArray);

    return(0);
}

次で実行:

valgrind -v --leak-check=full --show-reachable=yes ./a.out

回答:


378

「メモリリーク」を定義する方法は複数あります。特に、プログラマーの間で一般的に使用されている「メモリリーク」の2つの主要な定義があります。

「メモリリーク」の最初に一般的に使用される定義は、「プログラムが終了する前にメモリが割り当てられ、その後解放されなかった」です。ただし、多くのプログラマーは(正しく)、この定義に当てはまる特定の種類のメモリリークは実際にはいかなる種類の問題も引き起こさないため、真の「メモリリーク」と見なすべきではないと主張します。

間違いなく「メモリリーク」のより厳密な(そしてより有用な)定義は、「メモリが割り当てられ、割り当てられたメモリブロックへのポインタがなくなったため、その後解放できません。」つまり、ポインタがなくなったメモリを解放することはできません。したがって、このようなメモリは「メモリリーク」です。Valgrindは、この「メモリリーク」という用語のより厳密な定義を使用しています。これは、特に長期間有効なプロセスの場合に、ヒープの枯渇を引き起こす可能性のあるタイプのリークです。

Valgrindのリークレポート内の「まだ到達可能な」カテゴリは、「メモリリーク」の最初の定義にのみ当てはまる割り当てを指します。これらのブロックは解放されませんでしたが、プログラムがそれらのメモリブロックへのポインタを追跡し続けているため、解放された可能性があります(プログラマが望んでいた場合)。

一般に、「まだ到達可能な」ブロックについて心配する必要はありません。実際のメモリリークが引き起こすような問題は発生しません。たとえば、「まだ到達可能な」ブロックからヒープが枯渇する可能性は通常ありません。これは、これらのブロックが通常は1回限りの割り当てであり、その参照がプロセスの存続期間全体にわたって保持されるためです。プログラムを実行して割り当てられたすべてのメモリを確実に解放することはできますが、プロセスが終了するとオペレーティングシステムがプロセスのすべてのメモリを再利用するため、そうしても実際にはメリットはありません。これをtrueと比較してください メモリリーク。修正しないままにしておくと、プロセスの実行時間が長すぎるとプロセスがメモリ不足になるか、単にプロセスが必要以上のメモリを消費する可能性があります。

おそらく、リーク検出ツールがどのブロックが「まだ到達可能」であるかを判断できない場合(ただしValgrindはこれが可能)、またはオペレーティングシステムがすべてを再利用しない場合にのみ、すべての割り当てが「空き」に一致することを確認するのに役立ちます。終了プロセスのメモリ(Valgrindがこれを行うために移植されたすべてのプラットフォーム)。


munmap()が「まだ到達可能な」ブロックを消すために何をしているのかを推測できますか?

3
@crypto:munmap共有オブジェクトをアンロードした結果として呼び出された可能性があります。また、共有オブジェクトによって使用されるすべてのリソースは、アンロードされる前に解放される可能性があります。これは、「まだ到達可能」がmunmapケースで解放されている理由を説明することができます。ここでは推測しているだけです。確かに言うのに十分な情報がここにありません。
Dan Molding、

3
「まだ到達可能な」メモリがメモリリークと見なされる1つのケース:ヒープテーブルに割り当てられたメモリへのポインタを値として追加するハッシュテーブルがあるとします。テーブルに新しいエントリを挿入し続けても、不要になったエントリを削除して解放しない場合、無制限に大きくなり、ヒープメモリイベントがリークする可能性があります。これは、Javaまたは他のガベージコレクションされた言語で発生する可能性のあるメモリリークの場合です。
lvella 2015年

STLによって作成された「まだ到達可能な」ブロックに関するvalgrind FAQのこの回答も参照してください。 valgrind.org/docs/manual/faq.html#faq.reports
John Perry

5
「多くのプログラマーは(正しく)[リークされたメモリ]は実際には[a]の問題を引き起こさないため、真のメモリリークと見なすべきではないと主張する」 -Lol ...そのようなメモリリークでネイティブDLLを構築し、 Javaまたは.Netで消費します。Javaおよび.Netは、プログラムの存続期間中にDLLを数千回ロードおよびアンロードします。DLLがリロードされるたびに、もう少しメモリがリークします。長時間実行されているプログラムは、最終的にメモリ不足になります。DebianのOpenJDKメンテナを狂わせます。OpenSSLの「良性の」メモリリークについて話し合っている間に、彼はOpenSSLメーリングリストでも同じことを述べました。
jww 2018

10

下部にpthreadファミリーのルーチンがあるため(ただし、特定のルーチンはわかりません)、実行を終了したjoinableとしていくつかのスレッドを起動したと思われます。

そのスレッドの終了状態情報は、を呼び出すまで利用できますpthread_join。したがって、メモリはプログラムの終了時に損失レコードに保持されますが、それを使用pthread_joinしてアクセスできるため、メモリに到達できます。

この分析が正しい場合は、これらのスレッドを切り離して起動するか、プログラムを終了する前にそれらを結合します。

編集:私はサンプルプログラムを実行しました(明らかな修正を行った後)。エラーは発生していませんが、次のとおりです。

==18933== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 4 from 4)
--18933-- 
--18933-- used_suppression:      2 dl-hack3-cond-1
--18933-- used_suppression:      2 glibc-2.5.x-on-SUSE-10.2-(PPC)-2a

これはdl-あなたが目にするものの多くに類似しているので、の抑制ファイルに関して解決策がある既知の問題を目にしていると思いますvalgrind。おそらく、システムが最新でないか、ディストリビューションがこれらのものを維持していません。(鉱山はubuntu 10.4、64ビットです)


私はあなたと同じように0エラーを受け取ります。「リーク」の情報については、リークの概要を確認してください。

@crypto:わかりません。あなたは私と同じ抑圧を持っているということですか?
Jens Gustedt

used_suppression:14 dl-hack3-cond-1 <-それは私が得たものです

6

still reachable意味がわからないようです。

still reachableも漏れではありません。何もする必要はありません。


24
これは、Valgrindが提供する他の動詞と競合し、技術的にも正しくありません。メモリはプログラムの終了時に「まだ到達可能」であり、リークの可能性がありました。プログラムの終了後にメモリを十分にクリーンアップしないRTOSで実行するコードをデバッグしている場合はどうなりますか?
Toymakerii 2013年

4
残念ながら、それは常に正しいとは限りません。たとえば、失われたファイル記述子はメモリリークとしてカウントできますが、valgrindはそれらを「まだ到達可能」として分類します。これは、おそらくそれらにつながるポインタがシステムテーブル内でまだアクセス可能であるためです。しかし、デバッグの目的では、実際の診断は「メモリリーク」です。
シアン

失われたファイル記述子は、定義上、メモリリークではありません。多分あなたは失われたFILEポインターについて話しているのですか?
ロシア語を採用

6

「まだ到達可能」の適切な説明は次のとおりです。

「まだ到達可能」とは、グローバル変数と静的ローカル変数に割り当てられたリークです。valgrindはグローバル変数と静的変数を追跡するため、「1回限りの」割り当てられたメモリ割り当てを除外できます。グローバル変数は、割り当てを1回割り当てた後、その割り当てを再度割り当てたことはありません。これは、無限に大きくならないという意味で、通常「リーク」ではありません。厳密な意味ではまだリークですが、知識を深めない限り、通常は無視できます。

割り当てが割り当てられ、解放されていないローカル変数は、ほとんど常にリークです。

ここに例があります

int foo(void)
{
    static char *working_buf = NULL;
    char *temp_buf;
    if (!working_buf) {
         working_buf = (char *) malloc(16 * 1024);
    }
    temp_buf = (char *) malloc(5 * 1024);

    ....
    ....
    ....

}

Valgrindは、working_bufを「まだ到達可能-16k」、temp_bufを「完全に失われた-5k」として報告します。


-1

今後の読者にとって、「Still Reachable」は、ファイルなどを閉じるのを忘れたことを意味する場合があります。元の質問ではそのようには見えませんが、必ずそうしたことを確認してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.