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输出中有另外一行,以前没有出现过:

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

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

Answers:


378

定义“内存泄漏”的方法不止一种。特别是,“内存泄漏”有两个主要定义,它们在程序员中普遍使用。

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

可以说,对“内存泄漏”的定义更严格(更有用):“内存已分配,并且由于程序不再具有指向已分配内存块的指针,因此无法随后释放。” 换句话说,您无法释放不再具有任何指针的内存。因此,这种内存是“内存泄漏”。Valgrind使用术语“内存泄漏”的更严格定义。这是可能导致大量堆耗尽的泄漏类型,特别是对于寿命长的进程。

Valgrind的泄漏报告中的“仍可实现”类别是指仅符合“内存泄漏”的第一个定义的分配。这些块没有被释放,但是它们可以被释放(如果程序员愿意的话),因为程序仍在跟踪指向那些存储块的指针。

通常,无需担心“仍可到达”的块。它们不会带来真正的内存泄漏可能导致的那种问题。例如,通常不存在从“仍可到达”的块中耗尽堆的可能性。这是因为这些块通常是一次性分配,在过程的整个生命周期中都保留对它们的引用。尽管可以检查并确保程序释放所有分配的内存,但是这样做通常没有任何实际好处,因为无论如何,操作系统将在进程终止后回收所有进程的内存。与真实对比 内存泄漏,如果不解决,可能会导致进程耗尽足够长的内存,或者只会导致进程消耗比必要更多的内存。

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


您能猜出munmap()正在做什么,从而使“仍可到达”的块消失吗?

3
@crypto:可能是munmap由于卸载共享对象而调用的。并且共享对象使用的所有资源都可能在卸载之前被释放。这可以解释为什么在这种munmap情况下“仍然可以到达”被释放了。我只是在推测。这里没有足够的信息可以肯定地说。
Dan Moulding

3
“仍然可到达”的内存的一种情况可以认为是内存泄漏:假设您有一个哈希表,在其中添加了指向堆分配的内存的指针作为值。如果您继续在表上插入新条目,但不再删除并释放不再需要的条目,则它可能会无限期增长,如果该内存在技术上“仍然可以到达”,则会泄漏堆内存事件。在这种情况下,您可能会遇到Java或其他垃圾收集语言的内存泄漏。
lvella

另请参见valgrind FAQ中有关STL创建的“仍可到达”块的答案。 valgrind.org/docs/manual/faq.html#faq.reports
John Perry

5
“许多程序员(正确地)认为[泄漏的内存]实际上并没有带来[a]问题,因此不应将其视为真正的内存泄漏” -大声笑...用这种内存泄漏构建本机DLL,然后让Java或.Net使用它。在程序生命周期中,Java和.Net会上载和卸载DLL数千次。每次重新加载DLL时,它将泄漏更多的内存。长时间运行的程序最终将耗尽内存。它使Debian的OpenJDK维护人员发狂。在我们讨论OpenSSL的“良性”内存泄漏时,他在OpenSSL邮件列表中说的也是相同的。
jww

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,64位)


像您一样,我收到0个错误。请检查泄漏摘要以获取有关“泄漏”的信息。

@crypto:我不明白。你是说你有和我一样的压抑?
詹斯·古斯特

used_suppression:14 dl-hack3-cond-1 <-这就是我得到的

6

您似乎不明白这still reachable意味着什么。

什么still reachable泄漏。您无需为此做任何事情。


24
这与Valgrind提供的其他语言相抵触,并且在技术上不正确。程序退出时,内存“仍可访问”,因此有可能泄漏。如果您要调试要在RTOS上运行的代码,该程序在退出程序后不能很好地清理内存,该怎么办?
Toymakerii 2013年

4
不幸的是,并非总是如此。例如,丢失的文件描述符可以算作内存泄漏,但是valgrind将它们归类为“仍可访问”,大概是因为指向它们的指针仍然可以在系统表中访问。但是出于调试目的,真正的诊断是“内存泄漏”。
Cyan

根据定义,丢失的文件描述符不是内存泄漏。也许您在谈论丢失的FILE指针?
受雇于俄罗斯

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

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

}

Valgrind将报告work_buf为“仍可到达-16k”,而temp_buf报告为“绝对丢失-5k”。


-1

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.