[英]Still Reachable Leak detected by 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 输出中多了一行,这在以前是不存在的:
由于 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
定义“内存泄漏”的方法不止一种。 特别是,“内存泄漏”有两个主要定义,在程序员中很常见。
“内存泄漏”的第一个常用定义是“内存已分配,但在程序终止之前并未随后释放”。 但是,许多程序员(正确地)争辩说,符合此定义的某些类型的内存泄漏实际上不会造成任何类型的问题,因此不应将其视为真正的“内存泄漏”。
“内存泄漏”的一个可以说更严格(也更有用)的定义是,“内存已分配,但随后无法释放,因为程序不再有任何指向已分配内存块的指针。” 换句话说,您无法释放不再有任何指针指向的内存。 因此,这种内存是“内存泄漏”。 Valgrind 对术语“内存泄漏”使用了这个更严格的定义。 这种泄漏可能会导致显着的堆耗尽,尤其是对于长期存在的进程。
Valgrind 泄漏报告中的“仍然可达”类别是指仅符合“内存泄漏”的第一个定义的分配。 这些块没有被释放,但它们本来可以被释放(如果程序员想要的话),因为程序仍然在跟踪指向这些内存块的指针。
一般来说,不需要担心“仍然可达”的块。 它们不会造成真正的内存泄漏可能导致的那种问题。 例如,通常不会从“仍然可达”的块中耗尽堆。 这是因为这些块通常是一次性分配的,在进程的整个生命周期中都会保留对它们的引用。 虽然您可以通过并确保您的程序释放所有分配的内存,但这样做通常没有实际好处,因为操作系统将在进程终止后回收进程的所有内存,无论如何。 将此与真正的内存泄漏进行对比,如果不加以修复,如果保持运行时间足够长,可能会导致进程耗尽内存,或者只会导致进程消耗比所需更多的内存。
确保所有分配具有匹配的“释放”可能唯一有用的时候是如果您的泄漏检测工具无法判断哪些块“仍然可以访问”(但 Valgrind 可以做到这一点)或者如果您的操作系统没有回收所有终止进程的内存(Valgrind 已被移植来执行此操作的所有平台)。
由于底部有一些来自 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-
thing 与您看到的大部分内容相似,我猜您看到了一个已知问题,该问题在valgrind
的抑制文件方面具有解决方案。 也许你的系统不是最新的,或者你的发行版没有维护这些东西。 (我的是 ubuntu 10.4,64 位)
这是“仍然可以访问”的正确解释:
“仍然可达”是分配给全局和静态局部变量的泄漏。 由于 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 会将working_buf 报告为“仍可到达- 16k”,将temp_buf 报告为“肯定丢失- 5k”。
你似乎不明白什么是still reachable
。
任何still reachable
东西都不是泄漏。 你不需要做任何事情。
对于未来的读者,“Still Reachable”可能意味着您忘记关闭文件之类的东西。 虽然在最初的问题中似乎不是这样,但你应该始终确保你已经这样做了。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.