仍然可以通过Valgrind检测泄漏

147 c valgrind pthreads

该块中提到的所有功能都是库函数.我怎样才能纠正这个内存泄漏?

它列在" 仍可达 "类别下.(还有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)
Run Code Online (Sandbox Code Playgroud)

Catch:一旦我运行我的程序,它没有内存泄漏,但它在Valgrind输出中有一个额外的行,之前没有出现:

由于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);
}
Run Code Online (Sandbox Code Playgroud)

运行:

valgrind -v --leak-check=full --show-reachable=yes ./a.out
Run Code Online (Sandbox Code Playgroud)

Dan*_*ing 353

定义"内存泄漏"的方法不止一种.特别是,程序员通常使用"内存泄漏"的两个主要定义.

"内存泄漏"的第一个常用定义是"内存已分配,并且在程序终止之前未被释放".然而,许多程序员(正确地)认为符合这个定义的某些类型的内存泄漏实际上并没有造成任何问题,因此不应该被认为是真正的 "内存泄漏".

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

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

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

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

  • @crypto:可能是因为卸载共享对象而调用`munmap`.并且共享对象使用的所有资源可能在卸载之前被释放.这可以解释为什么"仍然可以使用"在"munmap"案例中被释放.不过,我只是在猜测.这里没有足够的信息可以肯定地说. (3认同)
  • "仍然可以访问"内存可以被视为内存泄漏的一种情况:假设您有一个哈希表,您可以在其中添加指向堆分配内存的指针作为值.如果你继续在表上插入新条目,但不会删除并释放那些你不再需要的条目,它会无限增长,如果该内存"仍然可以访问",则会泄漏堆内存事件.这是Java或其他垃圾收集语言中可能存在的内存泄漏的情况. (3认同)
  • *“许多程序员(正确地)认为[泄漏的内存]实际上不会带来[a]问题,因此不应视为真正的内存泄漏” *-大声笑...用这种内存泄漏构建本机DLL,然后让Java或.Net使用它。Java和.Net在程序生命周期内数千次加载和卸载DLL。每次重新加载DLL时,它将泄漏更多的内存。长时间运行的程序最终将耗尽内存。它使Debian的OpenJDK维护人员发狂。在我们讨论OpenSSL的“良性”内存泄漏时,他在OpenSSL邮件列表中说的也是相同的。 (2认同)

Jen*_*edt 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
Run Code Online (Sandbox Code Playgroud)

因为这个dl-东西类似于你看到的很多东西,我想你会看到一个已知的问题,它有一个抑制文件的解决方案valgrind.也许您的系统不是最新的,或者您的发行版不保留这些内容.(我的是ubuntu 10.4,64bit)


Emp*_*ian 7

你似乎不明白是什么still reachable意思.

什么still reachable泄漏.你不需要做任何事情.

  • 这与Valgrind提供的其他verbage冲突以及技术上不正确.程序退出时内存"仍然可以访问",因此可能会泄漏.如果您正在调试代码以在RTOS上运行并且在程序退出后没有很好地清理内存,该怎么办? (22认同)
  • 不幸的是,这并非总是如此.例如,丢失文件描述符可以计为内存泄漏,但是valgrind将它们归类为"仍然可以访问",可能是因为导致它们的指针仍然可以在系统表中访问.但是出于调试的目的,真正的诊断是"内存泄漏". (4认同)

小智 6

这是“仍然可以访问”的正确解释:

“仍然可达”是分配给全局和静态局部变量的泄漏。由于 valgrind 跟踪全局变量和静态变量,因此它可以排除分配“一劳永逸”的内存分配。一个全局变量分配了一次分配并且从未重新分配该分配通常不是“泄漏”,因为它不会无限期地增长。从严格意义上讲,它仍然是泄漏,但通常可以忽略,除非您迂腐。

分配了分配而不是释放的局部变量几乎总是泄漏。

这是一个例子

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);

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

}
Run Code Online (Sandbox Code Playgroud)

Valgrind 会将working_buf 报告为“仍可到达- 16k”,将temp_buf 报告为“肯定丢失- 5k”。