web-dev-qa-db-ja.com

Valgrindがまだ到達可能なリークを検出

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

まだ到達可能」カテゴリの下にリストされています。 (さらに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)

Catch:プログラムを実行すると、メモリリークは発生しませんでしたが、Valgrind出力に1行追加されました。

Munmap()が原因で/lib/libgcc_s-4.4.4-20100630.so.1の0x5296fa0-0x52af438でsymを破棄

リークを修正できない場合、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
141
user191776

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

「メモリリーク」の最初によく使用される定義は、「メモリが割り当てられ、プログラムが終了する前にメモリが解放されなかった」ことです。ただし、多くのプログラマーは(当然)この定義に適合する特定のタイプのメモリリークは実際には問題を引き起こさないため、true "memory leaks"と見なすべきではないと主張します。

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

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

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

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

338
Dan Moulding

一番下にpthreadファミリーのルーチンがいくつかあるので(しかし、その特定の1つはわかりません)、私の推測では、実行を終了するスレッドを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ビットです)

10
Jens Gustedt

still reachableの意味を理解していないようです。

still reachableはすべてnotリークです。あなたはそれについて何もする必要はありません。

7

「到達可能」の適切な説明を次に示します。

「到達可能」とは、グローバル変数および静的ローカル変数に割り当てられたリークです。 valgrindはグローバル変数と静的変数を追跡するため、「一度忘れて」割り当てられたメモリ割り当てを除外できます。グローバル変数は、割り当てを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」として報告します。

3
Abbey Road