内存泄漏的含义
关于”内存泄漏“,有两种定义:
程序结束时,有指针变量ptr能访问到申请的堆内存,但没通过
free(ptr)
来释放。即“程序结束时知道它在哪里,但确实没释放它”。程序结束时,之前申请的堆内存无法被任何一个指针变量访问,导致无法访问这块堆内存空间,造成了泄漏。也就是“程序结束时不知道它在哪里,也就没办法释放它”。
Valgrind的Memcheck工具,融合了上述两种类型的定义,并细分为如下5种:
编号 | 内存泄漏类型 | 具体含义 | 说明 |
---|---|---|---|
1 | definitely lost | 很明显的内存泄漏 | 必须修复 |
2 | indirectly lost | 程序的一个基于指针的结构里存在内存泄漏 | 必须修复 |
3 | possibly lost | 全局或静态指针指向堆内存,指针往后偏移但仍在该内存内,并最终free | 必须修复 |
4 | still reachable | 全局或静态指针指向堆内存,最终没有free这块内存 | 通常不严重,可以不管 |
5 | suppressed | 被抑制的内存错误,基于传入的suppressed文件 | 通常是配置忽略第三方库的内存泄漏 |
容易想到,让OpenCV搭配Valgrind,检查和报告其内存泄漏:
我遇到的情况是OpenCV有内存泄漏,通过传入opencv.supp
文件可以抑制它产生的possibly lost内存错误(放到了suppressed类型中),still reachable错误则忽略不管。生成和使用opencv.supp
文件:
valgrind --gen-suppressions=all --leak-check=yes ./a.out 2>&1 | tee valgrind.out
sed '/==/ d' valgrind.out > opencv.supp
valgrind --leak-check=yes --suppressions=opencv.supp ./a.out
以下是探索(罗嗦)版本的内容:
Valgrind基本介绍
在Linux下用Valgrind检查自己代码的内存泄漏情况。Valgrind是一组工具的集合,其中最基本的、并且是默认的工具,是Memcheck,可以检查内存泄漏情况。
Valgrind是一组工具的集合:
- Memcheck:内存错误检查器。最主要的、默认的工具(不需要指定
--tool=memcheck
就会使用的) - 其他工具:Cachegrind / Callgrind / Helgrind / DTR / Massif / DHAT / SGcheck / BBV / ...,这些工具需要用
--tool=xxx
参数指定后才能使用
Valgrind支持的平台有很多:
X86/Linux, AMD64/Linux, ARM/Linux, ARM64/Linux, PPC32/Linux, PPC64/Linux, PPC64LE/Linux, S390/Linux, MIPS32/Linux, MIPS64/Linux, X86/Solaris, AMD64/Solaris, ARM/Android(2.3.x and later), ARM64/Android, X86/Android(4.0 and later), MIPS32/Android, X86/Darwin, AMD64/Darwin
使用Valgrind的Memcheck工具,基本步骤
- 编写自己的程序;
vim xxx.c
- 使用
-g
参数编译程序,用来提供行号信息、符号表等;
gcc xxx.c -g
- 使用valgrind调用第2步编译出的可执行程序,建议添加
--leak-check=yes
参数来获取详细信息
valgrind --leak-check=yes ./a.out [arg1, arg2, ...]
其中[arg1, arg2, ...]表示可选参数
Valgrind的输出格式简要说明
比如我的测试代码是:
#include <stdlib.h>
void f(void)
{
int* x = malloc(10 * sizeof(int));
x[10] = 0; // problem 1: heap block overrun(越界)
} // problem 2: memory leak -- x not freed(内存泄漏)
int main(void)
{
f();
return 0;
}
对应的Valgrind输出为:
==5738== Invalid write of size 4
==5738== at 0x400544: f (test.c:6)
==5738== by 0x400555: main (test.c:11)
==5738== Address 0x5204068 is 0 bytes after a block of size 40 alloc'd
==5738== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5738== by 0x400537: f (test.c:5)
==5738== by 0x400555: main (test.c:11)
说明:
- 每条错误信息中都有很多细节,仔细看
- 5738是进程编号(pid),通常来说不重要
- 第一行("Invalid write...") 表明了错误类型。这里的情况是,写到申请的堆内存(heap block)之外了,也就是越界。
- 接下来的几行是栈记录(stack trace),表明了问题出在哪里。栈记录可能会很多,甚至让人看得头疼,尤其是当你用了C++ STL的时候。这些信息从下往上看会好一点。如果栈记录不大,可以使用
--num-callers
参数让它多数出东西。 - 代码地址(比如0x400544)通常来说不重要,但有时候对于跟踪诡异的bug来说很重要。
- 一些重要的错误信息有第二个组件,描述了相关的内存地址。在这个例子中显示的是,被写的内存地址刚刚超过了代码中第5行
malloc()
分配的内存块的最后面。
通常是从最前面的错误开始修复,因为后面的出错很可能是前面的错误导致的。不然的化你会觉得Memcheck很难用。
Valgrind的Memcheck工具的内存泄漏错误类型举例
在valgrind的输出内容中,找到LEAK SUMMARY
字段,它下面汇总了各种类型的内存泄漏:
内存泄漏细分类型 | 含义 |
---|---|
definitely lost | 确信无疑的内存泄漏 |
indirectly lost | 间接的内存泄漏 |
possibly lost | 可能有内存泄漏 |
still reachable | 程序退出时仍然可以访问的内存,也是泄漏 |
suppressed | 被抑制的 |
(表格参考:Valgrind笔记(一)——Memcheck初探 )
在我这样一个小白看来,细分成这么多类型的内存泄漏,我其实不是很分得清楚各自的区别。能否举例说明呢?经过一番搜索,基于这篇博客:valgrind 所报的4种内存丢失到底是什么意思(简洁),我拆分和修改出以上4种类型的内存泄漏各自的例子,根据例子更容易理解。
例子0:definitely lost
definitely lost意思是:确信无疑的内存泄漏,缺少free/detelete/delete[]操作。
举例:
#include <stdio.h>
#include <stdlib.h>
int main()
{
float* data = (float*)malloc(sizeof(float)*5);
return 0;
}
gcc example2.c -g
valgrind --leak-check=yes ./a.out
==23257== Memcheck, a memory error detector
==23257== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==23257== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==23257== Command: ./a.out
==23257==
==23257==
==23257== HEAP SUMMARY:
==23257== in use at exit: 20 bytes in 1 blocks
==23257== total heap usage: 1 allocs, 0 frees, 20 bytes allocated
==23257==
==23257== 20 bytes in 1 blocks are definitely lost in loss record 1 of 1
==23257== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==23257== by 0x400577: main (example2.c:6)
==23257==
==23257== LEAK SUMMARY:
==23257== definitely lost: 20 bytes in 1 blocks
==23257== indirectly lost: 0 bytes in 0 blocks
==23257== possibly lost: 0 bytes in 0 blocks
==23257== still reachable: 0 bytes in 0 blocks
==23257== suppressed: 0 bytes in 0 blocks
==23257==
==23257== For counts of detected and suppressed errors, rerun with: -v
==23257== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
例子1:indirectly lost
indirectly lost,说的很玄乎,还是看例子比较直观:
#include <stdio.h>
#include <stdlib.h>
typedef struct Image {
int h, w, c;
float* data;
} Image;
int main()
{
Image* im = (Image*)malloc(sizeof(Image));
int h = 224, w = 224, c = 3;
im->h = h;
im->w = w;
im->c = c;
im->data = (float*)malloc(sizeof(float)*h*w*c);
return 0;
}
gcc example2.c -g
valgrind --leak-check=yes ./a.out
==23580== Memcheck, a memory error detector
==23580== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==23580== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==23580== Command: ./a.out
==23580==
==23580==
==23580== HEAP SUMMARY:
==23580== in use at exit: 602,136 bytes in 2 blocks
==23580== total heap usage: 2 allocs, 0 frees, 602,136 bytes allocated
==23580==
==23580== 602,136 (24 direct, 602,112 indirect) bytes in 1 blocks are definitely lost in loss record 2 of 2
==23580== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==23580== by 0x400537: main (example2.c:11)
==23580==
==23580== LEAK SUMMARY:
==23580== definitely lost: 24 bytes in 1 blocks
==23580== indirectly lost: 602,112 bytes in 1 blocks
==23580== possibly lost: 0 bytes in 0 blocks
==23580== still reachable: 0 bytes in 0 blocks
==23580== suppressed: 0 bytes in 0 blocks
==23580==
==23580== For counts of detected and suppressed errors, rerun with: -v
==23580== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
样例代码中,缺少的是:
free(im->data);
free(im);
因为没有free(im)
,因此im
对应的内存泄漏了;而im
对应的内存块里面,有一个data
字段,data
字段现在也没有被释放,因此,602,112字节说的是224*224*4*sizeof(float)
这么一大块内存是indirectly lost。需要注意的是,definitely lost
此时为24字节,如果调用了free(im)
也就是消除了当前的definitely lost
,则当前的indirectly lost
会升级为definitely lost
。
例子2:possibly lost
找到possibly lost的例子并不很容易。按官方说法是,指针指向malloc申请的内存,然后指针往后++,再free这个指针,就得到possibly lost。看看下面这两个例子,按照这个解释,其实分别得到possibly lost和definitely lost。
possibly lost:
#include <stdio.h>
#include <stdlib.h>
int* g_p1;
void fun()
{
g_p1 = (int*)malloc(sizeof(int)*10);
g_p1++;
}
int main()
{
fun();
free(g_p1);
return 0;
}
==24652== Memcheck, a memory error detector
==24652== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==24652== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==24652== Command: ./a.out
==24652==
==24652== Invalid free() / delete / delete[] / realloc()
==24652== at 0x4C2EDEB: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24652== by 0x4005AC: main (example2.c:15)
==24652== Address 0x5204044 is 4 bytes inside a block of size 40 alloc'd
==24652== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24652== by 0x400573: fun (example2.c:8)
==24652== by 0x40059D: main (example2.c:14)
==24652==
==24652==
==24652== HEAP SUMMARY:
==24652== in use at exit: 40 bytes in 1 blocks
==24652== total heap usage: 1 allocs, 1 frees, 40 bytes allocated
==24652==
==24652== 40 bytes in 1 blocks are possibly lost in loss record 1 of 1
==24652== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24652== by 0x400573: fun (example2.c:8)
==24652== by 0x40059D: main (example2.c:14)
==24652==
==24652== LEAK SUMMARY:
==24652== definitely lost: 0 bytes in 0 blocks
==24652== indirectly lost: 0 bytes in 0 blocks
==24652== possibly lost: 40 bytes in 1 blocks
==24652== still reachable: 0 bytes in 0 blocks
==24652== suppressed: 0 bytes in 0 blocks
==24652==
==24652== For counts of detected and suppressed errors, rerun with: -v
==24652== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
definitely lost:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int* p1 = (int*)malloc(sizeof(int)*10);
p1++;
free(p1);
return 0;
}
==24703== Memcheck, a memory error detector
==24703== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==24703== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==24703== Command: ./a.out
==24703==
==24703== Invalid free() / delete / delete[] / realloc()
==24703== at 0x4C2EDEB: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24703== by 0x40058C: main (example22.c:8)
==24703== Address 0x5204044 is 4 bytes inside a block of size 40 alloc'd
==24703== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24703== by 0x400577: main (example22.c:6)
==24703==
==24703==
==24703== HEAP SUMMARY:
==24703== in use at exit: 40 bytes in 1 blocks
==24703== total heap usage: 1 allocs, 1 frees, 40 bytes allocated
==24703==
==24703== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==24703== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24703== by 0x400577: main (example22.c:6)
==24703==
==24703== LEAK SUMMARY:
==24703== definitely lost: 40 bytes in 1 blocks
==24703== indirectly lost: 0 bytes in 0 blocks
==24703== possibly lost: 0 bytes in 0 blocks
==24703== still reachable: 0 bytes in 0 blocks
==24703== suppressed: 0 bytes in 0 blocks
==24703==
==24703== For counts of detected and suppressed errors, rerun with: -v
==24703== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
例子3:still reachable
和例子2类似,still reacheable类型的内存泄漏,和definitely lost类型的内存泄漏,我觉得仍然是一个是全局变量的指针、另一个是局部变量指针的区别。来看例子:
still reachable
#include <stdio.h>
#include <stdlib.h>
int* g_p1;
void fun()
{
g_p1 = (int*)malloc(sizeof(int)*10);
}
int main()
{
fun();
return 0;
}
==24892== Memcheck, a memory error detector
==24892== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==24892== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==24892== Command: ./a.out
==24892==
==24892==
==24892== HEAP SUMMARY:
==24892== in use at exit: 40 bytes in 1 blocks
==24892== total heap usage: 1 allocs, 0 frees, 40 bytes allocated
==24892==
==24892== LEAK SUMMARY:
==24892== definitely lost: 0 bytes in 0 blocks
==24892== indirectly lost: 0 bytes in 0 blocks
==24892== possibly lost: 0 bytes in 0 blocks
==24892== still reachable: 40 bytes in 1 blocks
==24892== suppressed: 0 bytes in 0 blocks
==24892== Reachable blocks (those to which a pointer was found) are not shown.
==24892== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==24892==
==24892== For counts of detected and suppressed errors, rerun with: -v
==24892== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
definitely lost
其实这个例子就是“例子0:definitely lost”的例子,2333:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int* g_p1 = (int*)malloc(sizeof(int)*10);
return 0;
}
==24984== Memcheck, a memory error detector
==24984== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==24984== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==24984== Command: ./a.out
==24984==
==24984==
==24984== HEAP SUMMARY:
==24984== in use at exit: 40 bytes in 1 blocks
==24984== total heap usage: 1 allocs, 0 frees, 40 bytes allocated
==24984==
==24984== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==24984== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24984== by 0x400537: main (example33.c:6)
==24984==
==24984== LEAK SUMMARY:
==24984== definitely lost: 40 bytes in 1 blocks
==24984== indirectly lost: 0 bytes in 0 blocks
==24984== possibly lost: 0 bytes in 0 blocks
==24984== still reachable: 0 bytes in 0 blocks
==24984== suppressed: 0 bytes in 0 blocks
==24984==
==24984== For counts of detected and suppressed errors, rerun with: -v
==24984== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
简要总结Memcheck的内存泄漏类型
- definitely lost: 局部变量,用了malloc/calloc申请的堆内存,没有释放
- indirectly lost: 某个局部变量结构体对象本身是从malloc/calloc申请的堆内存,没有释放;并且,这个结构体的某个成员是个指针,也是用malloc/calloc从堆上申请的内存,也没有释放。此时,这个成员指向的内存,是indirectly lost。如果这个结构体对象被释放了,那么这个成员指向的内存,就是definitely lost。
- possibly lost:全局变量,指针类型,在某个函数中用malloc/calloc申请了堆内存给它,然后指针往后偏移了一部分,但是仍然处于这块堆内存里面,并且最后free了这个指针。那么这是possibly lost。
- still reachable:全局变量,指针类型,在某个函数中用malloc/calloc申请了堆内存给它,并且这个指针没有偏移;但是呢,最终程序没有free掉这个指针。这就是still reachable。
不管是以上哪种类型的内存泄漏,最理想最安全的做法都应该是,修复其中每种错误,确保没有内存泄漏。然而事实上也许并不容易,因为一旦你的程序用了第三方库,而这个第三方库有内存泄漏,那就需要搞清楚是谁写了bug。典型的例子是OpenCV。
OpenCV内存泄漏,例子1
测试环境:ubuntu16.04
opencv版本:sudo apt install libopencv-dev,2.4.9
#include <stdio.h>
#include <stdlib.h>
#include <opencv2/imgproc/imgproc_c.h>
#include <opencv2/highgui/highgui_c.h>
int main()
{
const char* filename = "cat.jpg";
IplImage* im = cvLoadImage(filename, -1);
cvReleaseImage(&im);
return 0;
}
Makefile:
CC=clang-3.5
OPENCV=opencv #apt-get's opencv
#OPENCV=/usr/local/opencv-4.1.1/lib/pkgconfig/opencv4.pc
#CFLAGS=-Wall `pkg-config --cflags $(OPENCV)` -O3 -flto -ffast-math
CFLAGS=-Wall `pkg-config --cflags $(OPENCV)` -O0 -g
LDFLAGS=`pkg-config --libs $(OPENCV)` -lm
#VPATH=./src/
OBJ=example4.o
all: a.out
a.out: $(OBJ)
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
.PHONY: clean
clean:
rm -rf $(OBJ) a.out
编译运行:如果开启--leak-check=yes
,输出内容特别多,这里没有开启
make
valgrind ./a.out
输出:
==26429== Memcheck, a memory error detector
==26429== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==26429== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==26429== Command: ./a.out
==26429==
==26429==
==26429== HEAP SUMMARY:
==26429== in use at exit: 228,088 bytes in 931 blocks
==26429== total heap usage: 4,282 allocs, 3,351 frees, 2,267,105 bytes allocated
==26429==
==26429== LEAK SUMMARY:
==26429== definitely lost: 0 bytes in 0 blocks
==26429== indirectly lost: 0 bytes in 0 blocks
==26429== possibly lost: 1,352 bytes in 18 blocks
==26429== still reachable: 226,736 bytes in 913 blocks
==26429== of which reachable via heuristic:
==26429== newarray : 1,536 bytes in 16 blocks
==26429== suppressed: 0 bytes in 0 blocks
==26429== Rerun with --leak-check=full to see details of leaked memory
==26429==
==26429== For counts of detected and suppressed errors, rerun with: -v
==26429== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
看到以上输出中的"possibly lost"和"still reachable"后,显然是OpenCV源码中有内存泄漏,至于在哪里并不清楚。
尝试切换opencv版本,从2.4.9切换到自行编译的3.4.5,依然是上述代码,valgrind输出为:
==27765== Memcheck, a memory error detector
==27765== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==27765== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==27765== Command: ./a.out
==27765==
./a.out: error while loading shared libraries: libopencv_dnn.so.3.4: cannot open shared object file: No such file or directory
==27765== Jump to the invalid address stated on the next line
==27765== at 0x566: ???
==27765== by 0x401044C: _dl_signal_error (dl-error.c:125)
==27765== by 0x400F069: _dl_map_object_deps (dl-deps.c:686)
==27765== by 0x4003A28: dl_main (rtld.c:1647)
==27765== by 0x40198AC: _dl_sysdep_start (dl-sysdep.c:249)
==27765== by 0x4001C29: _dl_start_final (rtld.c:323)
==27765== by 0x4001C29: _dl_start (rtld.c:429)
==27765== by 0x4000C37: ??? (in /lib/x86_64-linux-gnu/ld-2.23.so)
==27765== Address 0x566 is not stack'd, malloc'd or (recently) free'd
==27765==
==27765==
==27765== Process terminating with default action of signal 11 (SIGSEGV)
==27765== Bad permissions for mapped region at address 0x566
==27765== at 0x566: ???
==27765== by 0x401044C: _dl_signal_error (dl-error.c:125)
==27765== by 0x400F069: _dl_map_object_deps (dl-deps.c:686)
==27765== by 0x4003A28: dl_main (rtld.c:1647)
==27765== by 0x40198AC: _dl_sysdep_start (dl-sysdep.c:249)
==27765== by 0x4001C29: _dl_start_final (rtld.c:323)
==27765== by 0x4001C29: _dl_start (rtld.c:429)
==27765== by 0x4000C37: ??? (in /lib/x86_64-linux-gnu/ld-2.23.so)
==27765==
==27765== HEAP SUMMARY:
==27765== in use at exit: 0 bytes in 0 blocks
==27765== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==27765==
==27765== All heap blocks were freed -- no leaks are possible
==27765==
==27765== For counts of detected and suppressed errors, rerun with: -v
==27765== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
看起来,opencv3.4.5相比于opencv2.4.9,减少一些内存泄漏呀。
OpenCV内存泄漏,例子2
这次的例子,不仅仅读取和释放图像,还show图像(基于opencv3.4.5):
#include <stdio.h>
#include <stdlib.h>
#include <opencv2/imgproc/imgproc_c.h>
#include <opencv2/highgui/highgui_c.h>
int main()
{
const char* filename = "cat.jpg";
IplImage* im = cvLoadImage(filename, -1);
cvShowImage("cat", im);
cvReleaseImage(&im);
cvWaitKey(0);
return 0;
}
valgrind输出:
==28062== Memcheck, a memory error detector
==28062== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==28062== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==28062== Command: ./a.out
==28062==
==28062==
==28062== HEAP SUMMARY:
==28062== in use at exit: 1,726,832 bytes in 16,877 blocks
==28062== total heap usage: 100,861 allocs, 83,984 frees, 8,410,822 bytes allocated
==28062==
==28062== LEAK SUMMARY:
==28062== definitely lost: 0 bytes in 0 blocks
==28062== indirectly lost: 0 bytes in 0 blocks
==28062== possibly lost: 4,740 bytes in 50 blocks
==28062== still reachable: 1,631,380 bytes in 16,084 blocks
==28062== of which reachable via heuristic:
==28062== length64 : 5,376 bytes in 87 blocks
==28062== newarray : 2,096 bytes in 51 blocks
==28062== suppressed: 0 bytes in 0 blocks
==28062== Rerun with --leak-check=full to see details of leaked memory
==28062==
==28062== For counts of detected and suppressed errors, rerun with: -v
==28062== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
看到如上输出,看来OpenCV的内存泄漏问题还是没有完全解决。
配置Valgrind,忽略OpenCV的内存泄漏
不得不说,自行去翻看Valgrind中配置memcheck的suppression的文档感觉很繁琐...懒人做法是,让valgrind自己输出需要supprress的每一个配置,重定向到文件,然后去掉额外没用的打印输出内容,保存为opencv-3.4.5.supp
文件,下次给valgrind用,就可以忽略opencv的内存泄漏了。
#输出内存错误检测结果,并夹带suppression的配置一并输出;重定向到文件
valgrind --gen-suppressions=all --leak-check=yes ./a.out 2>&1 | tee valgrind.out
#去除文件中不必要的信息,并重定向到新的文件中
sed '/==/ d' valgrind.out > opencv-3.4.5.supp
(参考:Ignore part of code with valgrind - memcheck)
对应的代码:(没错,代码里仅仅是include了opencv,别的啥都没做,但是valgrind就是能检测到内存泄漏):
#include <stdio.h>
#include <stdlib.h>
#include <opencv2/imgproc/imgproc_c.h>
#include <opencv2/highgui/highgui_c.h>
int main()
{
return 0;
}
没有配置suppression文件的valgrind输出:
valgrind --leak-check=yes ./a.out
==6715== LEAK SUMMARY:
==6715== definitely lost: 0 bytes in 0 blocks
==6715== indirectly lost: 0 bytes in 0 blocks
==6715== possibly lost: 1,352 bytes in 18 blocks
==6715== still reachable: 237,176 bytes in 1,206 blocks
==6715== of which reachable via heuristic:
==6715== newarray : 1,536 bytes in 16 blocks
==6715== suppressed: 0 bytes in 0 blocks
==6715== Reachable blocks (those to which a pointer was found) are not shown.
==6715== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==6715==
==6715== For counts of detected and suppressed errors, rerun with: -v
==6715== ERROR SUMMARY: 18 errors from 18 contexts (suppressed: 0 from 0)
配置了suppression的valgrind输出:
valgrind --leak-check=yes --suppressions=opencv-3.4.5.supp ./a.out
==6746== HEAP SUMMARY:
==6746== in use at exit: 238,528 bytes in 1,224 blocks
==6746== total heap usage: 6,218 allocs, 4,994 frees, 666,594 bytes allocated
==6746==
==6746== LEAK SUMMARY:
==6746== definitely lost: 0 bytes in 0 blocks
==6746== indirectly lost: 0 bytes in 0 blocks
==6746== possibly lost: 0 bytes in 0 blocks
==6746== still reachable: 237,176 bytes in 1,206 blocks
==6746== of which reachable via heuristic:
==6746== newarray : 1,536 bytes in 16 blocks
==6746== suppressed: 1,352 bytes in 18 blocks
==6746== Reachable blocks (those to which a pointer was found) are not shown.
==6746== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==6746==
==6746== For counts of detected and suppressed errors, rerun with: -v
==6746== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 18 from 18)
没错,possibily lost从1352字节减少到0字节,总算把一部分内存泄漏的锅甩给OpenCV了!当然,still reachable没啥变化。。
这里贴一下我导出的opencv的suppression配置文件的内容(opencv-3.4.5.supp
):
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
fun:g_malloc0
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_type_register_fundamental
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:call_init.part.0
fun:call_init
fun:_dl_init
obj:/lib/x86_64-linux-gnu/ld-2.23.so
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:malloc
fun:realloc
fun:g_realloc
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_type_register_fundamental
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:call_init.part.0
fun:call_init
fun:_dl_init
obj:/lib/x86_64-linux-gnu/ld-2.23.so
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
fun:g_malloc0
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_type_register_fundamental
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:call_init.part.0
fun:call_init
fun:_dl_init
obj:/lib/x86_64-linux-gnu/ld-2.23.so
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:malloc
fun:realloc
fun:g_realloc
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_type_register_fundamental
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:call_init.part.0
fun:call_init
fun:_dl_init
obj:/lib/x86_64-linux-gnu/ld-2.23.so
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
fun:g_malloc0
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_type_register_fundamental
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:call_init.part.0
fun:call_init
fun:_dl_init
obj:/lib/x86_64-linux-gnu/ld-2.23.so
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:malloc
fun:realloc
fun:g_realloc
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_type_register_fundamental
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:call_init.part.0
fun:call_init
fun:_dl_init
obj:/lib/x86_64-linux-gnu/ld-2.23.so
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
fun:g_malloc0
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_type_register_fundamental
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:call_init.part.0
fun:call_init
fun:_dl_init
obj:/lib/x86_64-linux-gnu/ld-2.23.so
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:malloc
fun:realloc
fun:g_realloc
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_type_register_fundamental
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:call_init.part.0
fun:call_init
fun:_dl_init
obj:/lib/x86_64-linux-gnu/ld-2.23.so
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:malloc
fun:g_malloc
fun:g_slice_alloc
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_type_add_interface_static
fun:gtk_button_get_type
fun:gtk_toggle_button_get_type
fun:gtk_module_init
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:malloc
fun:g_malloc
fun:g_slice_alloc
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_type_add_interface_static
fun:gtk_icon_view_get_type
fun:gtk_module_init
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:malloc
fun:g_malloc
fun:g_slice_alloc
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_type_add_interface_static
fun:gtk_icon_view_get_type
fun:gtk_module_init
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
fun:g_malloc0
fun:g_type_class_ref
fun:g_type_class_ref
fun:g_param_spec_flags
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
fun:g_type_class_ref
fun:gtk_module_init
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
fun:g_closure_invoke
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_signal_emit_valist
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
fun:g_malloc0
fun:g_type_class_ref
fun:g_type_class_ref
fun:g_param_spec_enum
obj:/usr/lib/x86_64-linux-gnu/libgdk-3.so.0.1800.9
fun:g_type_class_ref
fun:g_type_class_ref
fun:g_object_new_valist
fun:g_object_new
obj:/usr/lib/x86_64-linux-gnu/libgdk-3.so.0.1800.9
obj:/usr/lib/x86_64-linux-gnu/libgdk-3.so.0.1800.9
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:malloc
fun:g_malloc
fun:g_slice_alloc
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_type_add_interface_static
fun:gtk_button_get_type
fun:gtk_toggle_button_get_type
fun:gtk_module_init
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:realloc
fun:g_realloc
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_type_register_static
fun:g_flags_register_static
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
fun:g_type_class_ref
fun:g_object_new_valist
fun:g_object_new
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
fun:g_malloc0
fun:g_type_class_ref
fun:g_type_class_ref
fun:g_type_create_instance
fun:g_param_spec_internal
fun:g_param_spec_object
obj:/usr/lib/x86_64-linux-gnu/libgdk-3.so.0.1800.9
fun:g_type_class_ref
fun:g_object_newv
fun:g_object_new
fun:gdk_display_manager_get
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
fun:g_malloc0
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:call_init.part.0
fun:call_init
fun:_dl_init
obj:/lib/x86_64-linux-gnu/ld-2.23.so
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
fun:g_malloc0
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_type_register_fundamental
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:call_init.part.0
fun:call_init
fun:_dl_init
obj:/lib/x86_64-linux-gnu/ld-2.23.so
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
fun:g_malloc0
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_type_register_fundamental
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:call_init.part.0
fun:call_init
fun:_dl_init
obj:/lib/x86_64-linux-gnu/ld-2.23.so
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
fun:g_malloc0
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_type_register_fundamental
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:call_init.part.0
fun:call_init
fun:_dl_init
obj:/lib/x86_64-linux-gnu/ld-2.23.so
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
fun:g_malloc0
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_type_register_fundamental
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:call_init.part.0
fun:call_init
fun:_dl_init
obj:/lib/x86_64-linux-gnu/ld-2.23.so
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
fun:g_malloc0
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_type_register_fundamental
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:call_init.part.0
fun:call_init
fun:_dl_init
obj:/lib/x86_64-linux-gnu/ld-2.23.so
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
fun:g_malloc0
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_type_register_fundamental
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:call_init.part.0
fun:call_init
fun:_dl_init
obj:/lib/x86_64-linux-gnu/ld-2.23.so
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:malloc
fun:g_malloc
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_object_new_valist
fun:g_object_new
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
fun:g_malloc0
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_type_register_fundamental
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:call_init.part.0
fun:call_init
fun:_dl_init
obj:/lib/x86_64-linux-gnu/ld-2.23.so
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
fun:g_malloc0
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_type_register_fundamental
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:call_init.part.0
fun:call_init
fun:_dl_init
obj:/lib/x86_64-linux-gnu/ld-2.23.so
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:malloc
fun:g_malloc
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_object_new_valist
fun:g_object_new
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:realloc
fun:g_realloc
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_type_register_static
fun:g_param_type_register_static
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:call_init.part.0
fun:call_init
fun:_dl_init
obj:/lib/x86_64-linux-gnu/ld-2.23.so
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:malloc
fun:g_malloc
fun:g_memdup
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
fun:gtk_style_context_set_state
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_object_new_valist
fun:g_object_new
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:malloc
fun:g_malloc
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_object_new_valist
fun:g_object_new
obj:/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9
fun:gtk_style_new
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:realloc
fun:g_realloc
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_type_register_static
fun:g_enum_register_static
fun:atk_state_type_get_type
fun:atk_state_type_get_name
fun:atk_object_notify_state_change
fun:gtk_accessible_set_widget
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_object_new_valist
fun:g_object_new
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
fun:allocate_dtv
fun:_dl_allocate_tls
fun:allocate_stack
fun:pthread_create@@GLIBC_2.2.5
obj:/lib/x86_64-linux-gnu/libglib-2.0.so.0.4800.2
obj:/lib/x86_64-linux-gnu/libglib-2.0.so.0.4800.2
fun:g_thread_new
obj:/usr/lib/x86_64-linux-gnu/gio/modules/libdconfsettings.so
obj:/usr/lib/x86_64-linux-gnu/gio/modules/libdconfsettings.so
obj:/usr/lib/x86_64-linux-gnu/gio/modules/libdconfsettings.so
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_object_new_valist
fun:g_object_new
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
fun:allocate_dtv
fun:_dl_allocate_tls
fun:allocate_stack
fun:pthread_create@@GLIBC_2.2.5
obj:/lib/x86_64-linux-gnu/libglib-2.0.so.0.4800.2
obj:/lib/x86_64-linux-gnu/libglib-2.0.so.0.4800.2
fun:g_thread_new
obj:/lib/x86_64-linux-gnu/libglib-2.0.so.0.4800.2
fun:g_task_get_type
obj:/usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.4800.2
fun:g_bus_get_sync
obj:/usr/lib/x86_64-linux-gnu/gio/modules/libdconfsettings.so
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
fun:allocate_dtv
fun:_dl_allocate_tls
fun:allocate_stack
fun:pthread_create@@GLIBC_2.2.5
obj:/lib/x86_64-linux-gnu/libglib-2.0.so.0.4800.2
obj:/lib/x86_64-linux-gnu/libglib-2.0.so.0.4800.2
fun:g_thread_new
obj:/usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.4800.2
obj:/usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.4800.2
fun:g_bus_get_sync
obj:/usr/lib/x86_64-linux-gnu/gio/modules/libdconfsettings.so
obj:/usr/lib/x86_64-linux-gnu/gio/modules/libdconfsettings.so
fun:g_main_context_dispatch
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: possible
fun:realloc
fun:g_realloc
obj:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2
fun:g_type_register_static
fun:g_type_register_static_simple
fun:gtk_window_group_get_type
fun:gtk_window_group_new
fun:gtk_window_get_group
fun:gtk_main_do_event
obj:/usr/lib/x86_64-linux-gnu/libgdk-3.so.0.1800.9
fun:g_main_context_dispatch
obj:/lib/x86_64-linux-gnu/libglib-2.0.so.0.4800.2
}
那么,still reachable到底什么意思呢?
参考http://shigaro.org/2019/07/05/valgrind-2/#,
There is more than one way to define “memory leak”. In particular, there are two primary definitions of “memory leak” that are in common usage among programmers.
The first commonly used definition of “memory leak” is, “Memory was allocated and was not subsequently freed before the program terminated.” However, many programmers (rightly) argue that certain types of memory leaks that fit this definition don’t actually pose any sort of problem, and therefore should not be considered true “memory leaks”.
An arguably stricter (and more useful) definition of “memory leak” is, “Memory was allocated and cannot be subsequently freed because the program no longer has any pointers to the allocated memory block.” In other words, you cannot free memory that you no longer have any pointers to. Such memory is therefore a “memory leak”. Valgrind uses this stricter definition of the term “memory leak”. This is the type of leak which can potentially cause significant heap depletion, especially for long lived processes.
The “still reachable” category within Valgrind’s leak report refers to allocations that fit only the first definition of “memory leak”. These blocks were not freed, but they could have been freed (if the programmer had wanted to) because the program still was keeping track of pointers to those memory blocks.
In general, there is no need to worry about “still reachable” blocks. They don’t pose the sort of problem that true memory leaks can cause. For instance, there is normally no potential for heap exhaustion from “still reachable” blocks. This is because these blocks are usually one-time allocations, references to which are kept throughout the duration of the process’s lifetime. While you could go through and ensure that your program frees all allocated memory, there is usually no practical benefit from doing so since the operating system will reclaim all of the process’s memory after the process terminates, anyway. Contrast this with true memory leaks which, if left unfixed, could cause a process to run out of memory if left running long enough, or will simply cause a process to consume far more memory than is necessary.
翻译为:
定义”内存泄漏”的方式不止一种. 特别的, 在程序员间常用的主要有两种”内存泄漏”的定义.
第一种常用的定义是, “内存被分配, 随后没有在程序结束前被释放”. 但是, 很多程序员(正确地)主张说符合这一定义的内存泄漏并不会造成问题, 因此并不被认为是真正的内存泄漏.
“内存泄漏”的一种可能更为严格(也更有用)的定义是, “内存被分配后, 由于程序失去了指向被分配内存块的指针而无法被释放”. 换句话说, 你无法释放没有指针指向的内存. 所以这样的内存属于”内存泄漏”. Valgrind用的是这一更为严格的定义. 这类泄漏可能产生严重的堆损耗, 特别是在长期活动的进程中.
Valgrind的泄漏报告中”still reachable”分类指的是只满足第一类定义的内存分配. 这些内存块没有被释放, 但他们是可以被释放的(只要程序员愿意), 因为程序仍然保有指向这些内存块的指针.
一般而言, 不必担心”still reachable”的内存块. 他们不会带来真正的内存泄漏会导致的问题. 比如说, “still reachable”的内存块通常不会导致堆耗尽. 这是因为这些块都是单次分配, 程序在整个生命周期中都保留对他们的指向. 你当然可以梳理整个程序, 保证这些内存块都被释放, 但这实际并没什么好处, 因为操作系统会在进程结束后回收进程的全部内存. 与之相对, 如果真正的内存泄漏没有被修正, 那么就会导致一个进程在运行足够长时间后耗尽所有内存, 或者说消耗比它所必需的多得多的内存.
看来,still reachable类型的内存泄漏,意思是“本该释放的堆内存,并且一直有指针能够访问这些堆内存,但是到程序结束的时候,都没有释放掉”。
上面这大段的引用(英文原文),其实来在https://stackoverflow.com/questions/3840582/still-reachable-leak-detected-by-valgrind,这个SO的问题回答和评论中,还有人提到still reachable的一个特例:FILE指针。没错,当打开一个文件的时候,用了FILE* fp
,无论是读取还是写入,如果程序最终没有关闭这个fp,valgrind会把它算作“still reachable”。。。。其实这个说法也不严禁。经过实验我发现,在缺少fclose(fp)
的情况下:
-
如果是
FILE* fp = fopen("in.txt", "r")
(读取文件)- 文件"in.txt"存在,那么会有still reachable问题
- 不存在"in.txt",则没有still reachable问题
如果是
FILE* fp=fclose("in.txt", "w")
(写入文件),则存在still reachable问题(内存不足的极端情况没有测试,2333)
显然,上述关于文件读写的情况,是因为其内部实现中有使用缓冲区来存储需要读取或写入的内容,这个缓冲区应该是从堆上动态分配的内存空间,可以参考APUE(《unix环境高级编程)上的相关描述,例如这篇博客:fopen的默认缓冲大小和setvbuf 用法。
此外,这个SO回答下,还有人给出了still reachable的更精确定义:
Here is a proper explanation of "still reachable":
"Still reachable" are leaks assigned to global and static-local variables. Because valgrind tracks global and static variables it can exclude memory allocations that are assigned "once-and-forget". A global variable assigned an allocation once and never reassigned that allocation is typically not a "leak" in the sense that it does not grow indefinitely. It is still a leak in the strict sense, but can usually be ignored unless you are pedantic.
Local variables that are assigned allocations and not free'd are almost always leaks.
Here is an example
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 will report working_buf as "still reachable - 16k" and temp_buf as "definitely lost - 5k".
个人比较认同这个回答中的解释,"Still reachable" are leaks assigned to global and static-local variables(Still reachable对应的内存泄漏是说,全局或者static局部变量的指针,对应的内存泄漏)。
使用OpenCV官方的suppression配置文件来忽略内存泄漏
搞了半天发现自己民科了...opencv的github issue中早有人提出了类似“opencv你怎么在valgrind下有内存泄漏”的问题:OpenCV 3.3.0 memory leak for CLAHE Ubuntu 16.04,官方issue发言人也早有了回答:
alalek commented on 21 Sep 2017
Consider to use valgrind suppression files from here:
https://github.com/opencv/opencv/tree/master/platforms/scripts
果然官方的supp文件好用!粘贴opencv.supp
如下:
{
OpenCV-IPP static init
Memcheck:Cond
fun:ippicvGetCpuFeatures
fun:ippicvStaticInit
}
{
OpenCV-getInitializationMutex
Memcheck:Leak
...
fun:_ZN2cv22getInitializationMutexEv
}
{
OpenCV-SingletonBuffer
Memcheck:Leak
...
fun:_ZN2cv20allocSingletonBufferEm
}
{
OpenCV-SingletonNewBuffer
Memcheck:Leak
...
fun:_ZN2cv23allocSingletonNewBufferEm
}
{
OpenCV-getStdAllocator
Memcheck:Leak
...
fun:_ZN2cv3Mat15getStdAllocatorEv
}
{
OpenCV-getOpenCLAllocator
Memcheck:Leak
...
fun:_ZN2cv3ocl18getOpenCLAllocatorEv
}
{
OpenCV-getCoreTlsData
Memcheck:Leak
fun:_Znwm
fun:_ZN2cv14getCoreTlsDataEv
}
{
OpenCV-TLS-getTlsStorage
Memcheck:Leak
...
fun:_ZN2cvL13getTlsStorageEv
}
{
OpenCV-TLS-getData()
Memcheck:Leak
...
fun:*setData*
fun:_ZNK2cv16TLSDataContainer7getDataEv
}
{
OpenCV-parallel_for-reconfigure
Memcheck:Leak
...
fun:_ZN2cv10ThreadPool12reconfigure_Ej
}
{
OpenCV-parallel_for-instance
Memcheck:Leak
fun:_Znwm
fun:*instance*
...
fun:_ZN2cv13parallel_for_ERKNS_5RangeERKNS_16ParallelLoopBodyEd
}
{
OpenCV-parallel_for-setNumThreads()
Memcheck:Leak
...
fun:_ZN2cv13setNumThreadsEi
}
{
OpenCV-parallel_for-getNumThreads()
Memcheck:Leak
...
fun:_ZN2cv13getNumThreadsEv
}
{
OpenCV-getIPPSingelton
Memcheck:Leak
...
fun:_ZN2cv3ippL15getIPPSingeltonEv
}
{
OpenCV-getGlobalMatOpInitializer
Memcheck:Leak
fun:_Znwm
fun:_ZN2cvL25getGlobalMatOpInitializerEv
}
{
OpenCV-CoreTLSData
Memcheck:Leak
...
fun:_ZNK2cv7TLSDataINS_11CoreTLSDataEE3getEv
}
{
OpenCV-getThreadID()
Memcheck:Leak
...
fun:_ZN2cv5utils11getThreadIDEv
}
{
OpenCV-ThreadID
Memcheck:Leak
fun:_Znwm
fun:_ZNK2cv7TLSDataINS_12_GLOBAL__N_18ThreadIDEE18createDataInstanceEv
}
{
OpenCV-ThreadID-TLS
Memcheck:Leak
fun:_Znwm
fun:getThreadIDTLS
}
{
OpenCV-CoreTLS
Memcheck:Leak
fun:_Znwm
fun:_ZNK2cv7TLSDataINS_11CoreTLSDataEE18createDataInstanceEv
}
{
OpenCV-UMatDataAutoLockerTLS
Memcheck:Leak
...
fun:_ZN2cvL21getUMatDataAutoLockerEv
}
{
OpenCV-haveOpenCL
Memcheck:Leak
...
fun:_ZN2cv3ocl10haveOpenCLEv
}
{
OpenCV-DNN-getLayerFactoryMutex
Memcheck:Leak
...
fun:_ZN2cv3dnn*L20getLayerFactoryMutexEv
}
{
OpenCV-ocl::Context
Memcheck:Leak
...
fun:_ZN2cv3ocl7Context10getDefaultEb
}
{
OpenCV-ocl::Device
Memcheck:Leak
...
fun:_ZN2cv3ocl6Device10getDefaultEv
}
{
OpenCV-ocl::Queue
Memcheck:Leak
...
fun:_ZN2cv3ocl5Queue6createERKNS0_7ContextERKNS0_6DeviceE
}
{
OpenCV-ocl::Program
Memcheck:Leak
...
fun:_ZN2cv3ocl6Kernel6createEPKcRKNS0_7ProgramE
}
{
OpenCV-ocl::ProgramEntry
Memcheck:Leak
...
fun:_ZNK2cv3ocl8internal12ProgramEntrycvRNS0_13ProgramSourceEEv
}
{
OpenCV-ocl::Context::getProg
Memcheck:Leak
...
fun:_ZN2cv3ocl7Context7getProgERKNS0_13ProgramSourceERKNS_6StringERS5_
}
{
OpenCV-getTraceManager()
Memcheck:Leak
...
fun:getTraceManagerCallOnce
}
{
OpenCV-ITT
Memcheck:Leak
...
fun:__itt_*create*
}
{
OpenCV-gtk_init
Memcheck:Leak
...
fun:gtk_init
fun:cvInitSystem
}
{
OpenCV-FFmpeg-swsscale
Memcheck:Addr16
...
fun:sws_scale
fun:_ZN20CvVideoWriter_FFMPEG10writeFrameEPKhiiiii
fun:cvWriteFrame_FFMPEG
}
{
OpenCV-GStreamer-gst_init
Memcheck:Leak
...
fun:gst_init
}
{
OpenCV-GStreamer-gst_deinit
Memcheck:Leak
...
fun:gst_deinit
}
{
OpenCV-GStreamer-gst_init_check
Memcheck:Leak
...
fun:gst_init_check
}
{
OpenCV-GStreamer-gst_parse_launch_full-reachable
Memcheck:Leak
match-leak-kinds: reachable
...
fun:gst_parse_launch_full
}
{
OpenCV-OpenEXR-ThreadPool
Memcheck:Leak
fun:_Znwm
fun:_ZN16IlmThread_opencv10ThreadPoolC1Ej
fun:_ZN16IlmThread_opencv10ThreadPool16globalThreadPoolEv
}
{
OpenCV-test-gapi-thread-tls
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
fun:allocate_dtv
fun:_dl_allocate_tls
}
试试看?测试代码,仅仅是include了opencv而不做任何事情的:
#include <stdio.h>
#include <stdlib.h>
#include <opencv2/imgproc/imgproc_c.h>
#include <opencv2/highgui/highgui_c.h>
int main()
{
return 0;
}
结果输出:
==8700== LEAK SUMMARY:
==8700== definitely lost: 0 bytes in 0 blocks
==8700== indirectly lost: 0 bytes in 0 blocks
==8700== possibly lost: 1,352 bytes in 18 blocks
==8700== still reachable: 237,120 bytes in 1,204 blocks
==8700== of which reachable via heuristic:
==8700== newarray : 1,536 bytes in 16 blocks
==8700== suppressed: 56 bytes in 2 blocks
==8700== Reachable blocks (those to which a pointer was found) are not shown.
==8700== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==8700==
==8700== For counts of detected and suppressed errors, rerun with: -v
==8700== ERROR SUMMARY: 18 errors from 18 contexts (suppressed: 0 from 0)
仍然看到possibly lost,并不完美。