仍然可以通过Valgrind检测泄漏

时间:2010-10-01 15:22:20

标签: c pthreads 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输出中有一个额外的行,之前没有出现:

  

在0x5296fa0-0x52af438丢弃符号   在/lib/libgcc_s-4.4.4-20100630.so.1中   由于munmap()

如果泄漏无法纠正,有人可以解释为什么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

5 个答案:

答案 0 :(得分:326)

定义“内存泄漏”的方法不止一种。特别是,“内存泄漏”有两个主要定义,这些定义在程序员中是常用的。

“内存泄漏”的第一个常用定义是“内存已分配,并且在程序终止之前未被释放”。但是,许多程序员(正确地)认为适合此定义的某些类型的内存泄漏实际上不会造成任何类型的问题,因此不应被视为 true “内存泄漏”。

“内存泄漏”的一个可以说更严格(也更有用)的定义是,“内存已分配且无法随后被释放,因为程序不再有指向已分配内存块的指针。”换句话说,你不能释放你不再有任何指针的记忆。因此,这种存储器是“存储器泄漏”。 Valgrind使用了“内存泄漏”一词的更严格定义。这种泄漏类型可能会导致严重的堆耗尽,特别是对于长寿命流程。

Valgrind泄漏报告中的“仍可达”类别是指仅适合“内存泄漏”的第一个定义的分配。这些块没有被释放,但它们本可以被释放(如果程序员想要的话),因为程序仍然跟踪指向这些内存块的指针。

一般情况下,无需担心“仍然可以访问”的块。它们不会造成 true 内存泄漏可能导致的问题。例如,“仍然可以访问”的块通常没有堆耗尽的可能性。这是因为这些块通常是一次性分配,在过程的整个生命周期中都会保留对它的引用。虽然您可以通过并确保您的程序释放所有分配的内存,但这样做通常没有任何实际好处,因为操作系统将在进程终止后回收所有进程的内存,无论如何。将此与 true 内存泄漏进行对比,如果保持不动,可能会导致进程在运行时运行时间过长,或者只会导致进程消耗的内存远远超过必要的内存。 / p>

可能唯一一次确保所有分配都具有匹配的“释放”是唯一的时间,如果您的泄漏检测工具无法判断哪些块“仍然可以访问”(但Valgrind可以执行此操作)或者您的操作系统没有回收所有终止进程的内存(Valgrind已移植到所有平台上执行此操作)。

答案 1 :(得分:10)

由于底层的pthread系列有一些例程(但我不知道那个特定的例程),我的猜测是你已经启动了一些已经终止执行的可连接线程。

在您致电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,64bit)

答案 2 :(得分:7)

您似乎不了解still reachable的含义。

任何still reachable 都不是泄漏。你不需要做任何事情。

答案 3 :(得分:3)

以下是&#34;仍然可以访问的正确解释&#34;:

&#34;仍然可以到达&#34;是分配给全局和静态局部变量的泄漏。因为valgrind跟踪全局变量和静态变量,所以它可以排除分配了“一劳永逸”的内存分配。一个全局变量分配一次分配并且永远不会重新分配该分配通常不是&#34;泄漏&#34;从某种意义上说,它不会无限增长。从严格意义上来说,这仍然是一种泄漏,但除非你是迂腐的,否则通常可以忽略它。

分配了分配而非免费的局部变量几乎总是泄漏。

这是一个例子

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为&#34;仍然可以访问 - 16k&#34;和temp_buf一样,#34;肯定会丢失--5k&#34;。

答案 4 :(得分:-1)

对于将来的读者来说,“仍可到达”可能意味着您忘记关闭文件之类的东西。尽管原始问题似乎并非如此,但您应始终确保已完成操作。