內(nèi)存泄漏的含義
關(guān)于”內(nèi)存泄漏“,有兩種定義:
程序結(jié)束時(shí),有指針變量ptr能訪問(wèn)到申請(qǐng)的堆內(nèi)存,但沒(méi)通過(guò)
free(ptr)來(lái)釋放。即“程序結(jié)束時(shí)知道它在哪里,但確實(shí)沒(méi)釋放它”。程序結(jié)束時(shí),之前申請(qǐng)的堆內(nèi)存無(wú)法被任何一個(gè)指針變量訪問(wèn),導(dǎo)致無(wú)法訪問(wèn)這塊堆內(nèi)存空間,造成了泄漏。也就是“程序結(jié)束時(shí)不知道它在哪里,也就沒(méi)辦法釋放它”。
Valgrind的Memcheck工具,融合了上述兩種類型的定義,并細(xì)分為如下5種:
| 編號(hào) | 內(nèi)存泄漏類型 | 具體含義 | 說(shuō)明 |
|---|---|---|---|
| 1 | definitely lost | 很明顯的內(nèi)存泄漏 | 必須修復(fù) |
| 2 | indirectly lost | 程序的一個(gè)基于指針的結(jié)構(gòu)里存在內(nèi)存泄漏 | 必須修復(fù) |
| 3 | possibly lost | 全局或靜態(tài)指針指向堆內(nèi)存,指針往后偏移但仍在該內(nèi)存內(nèi),并最終free | 必須修復(fù) |
| 4 | still reachable | 全局或靜態(tài)指針指向堆內(nèi)存,最終沒(méi)有free這塊內(nèi)存 | 通常不嚴(yán)重,可以不管 |
| 5 | suppressed | 被抑制的內(nèi)存錯(cuò)誤,基于傳入的suppressed文件 | 通常是配置忽略第三方庫(kù)的內(nèi)存泄漏 |
容易想到,讓OpenCV搭配Valgrind,檢查和報(bào)告其內(nèi)存泄漏:

我遇到的情況是OpenCV有內(nèi)存泄漏,通過(guò)傳入opencv.supp文件可以抑制它產(chǎn)生的possibly lost內(nèi)存錯(cuò)誤(放到了suppressed類型中),still reachable錯(cuò)誤則忽略不管。生成和使用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
以下是探索(羅嗦)版本的內(nèi)容:
Valgrind基本介紹
在Linux下用Valgrind檢查自己代碼的內(nèi)存泄漏情況。Valgrind是一組工具的集合,其中最基本的、并且是默認(rèn)的工具,是Memcheck,可以檢查內(nèi)存泄漏情況。
Valgrind是一組工具的集合:
- Memcheck:內(nèi)存錯(cuò)誤檢查器。最主要的、默認(rèn)的工具(不需要指定
--tool=memcheck就會(huì)使用的) - 其他工具:Cachegrind / Callgrind / Helgrind / DTR / Massif / DHAT / SGcheck / BBV / ...,這些工具需要用
--tool=xxx參數(shù)指定后才能使用
Valgrind支持的平臺(tái)有很多:
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工具,基本步驟
- 編寫(xiě)自己的程序;
vim xxx.c
- 使用
-g參數(shù)編譯程序,用來(lái)提供行號(hào)信息、符號(hào)表等;
gcc xxx.c -g
- 使用valgrind調(diào)用第2步編譯出的可執(zhí)行程序,建議添加
--leak-check=yes參數(shù)來(lái)獲取詳細(xì)信息
valgrind --leak-check=yes ./a.out [arg1, arg2, ...]
其中[arg1, arg2, ...]表示可選參數(shù)
Valgrind的輸出格式簡(jiǎn)要說(shuō)明
比如我的測(cè)試代碼是:
#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(內(nèi)存泄漏)
int main(void)
{
f();
return 0;
}
對(duì)應(yīng)的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)
說(shuō)明:
- 每條錯(cuò)誤信息中都有很多細(xì)節(jié),仔細(xì)看
- 5738是進(jìn)程編號(hào)(pid),通常來(lái)說(shuō)不重要
- 第一行("Invalid write...") 表明了錯(cuò)誤類型。這里的情況是,寫(xiě)到申請(qǐng)的堆內(nèi)存(heap block)之外了,也就是越界。
- 接下來(lái)的幾行是棧記錄(stack trace),表明了問(wèn)題出在哪里。棧記錄可能會(huì)很多,甚至讓人看得頭疼,尤其是當(dāng)你用了C++ STL的時(shí)候。這些信息從下往上看會(huì)好一點(diǎn)。如果棧記錄不大,可以使用
--num-callers參數(shù)讓它多數(shù)出東西。 - 代碼地址(比如0x400544)通常來(lái)說(shuō)不重要,但有時(shí)候?qū)τ诟櫾幃惖腷ug來(lái)說(shuō)很重要。
- 一些重要的錯(cuò)誤信息有第二個(gè)組件,描述了相關(guān)的內(nèi)存地址。在這個(gè)例子中顯示的是,被寫(xiě)的內(nèi)存地址剛剛超過(guò)了代碼中第5行
malloc()分配的內(nèi)存塊的最后面。
通常是從最前面的錯(cuò)誤開(kāi)始修復(fù),因?yàn)楹竺娴某鲥e(cuò)很可能是前面的錯(cuò)誤導(dǎo)致的。不然的化你會(huì)覺(jué)得Memcheck很難用。
Valgrind的Memcheck工具的內(nèi)存泄漏錯(cuò)誤類型舉例
在valgrind的輸出內(nèi)容中,找到LEAK SUMMARY字段,它下面匯總了各種類型的內(nèi)存泄漏:
| 內(nèi)存泄漏細(xì)分類型 | 含義 |
|---|---|
| definitely lost | 確信無(wú)疑的內(nèi)存泄漏 |
| indirectly lost | 間接的內(nèi)存泄漏 |
| possibly lost | 可能有內(nèi)存泄漏 |
| still reachable | 程序退出時(shí)仍然可以訪問(wèn)的內(nèi)存,也是泄漏 |
| suppressed | 被抑制的 |
(表格參考:Valgrind筆記(一)——Memcheck初探 )
在我這樣一個(gè)小白看來(lái),細(xì)分成這么多類型的內(nèi)存泄漏,我其實(shí)不是很分得清楚各自的區(qū)別。能否舉例說(shuō)明呢?經(jīng)過(guò)一番搜索,基于這篇博客:valgrind 所報(bào)的4種內(nèi)存丟失到底是什么意思(簡(jiǎn)潔),我拆分和修改出以上4種類型的內(nèi)存泄漏各自的例子,根據(jù)例子更容易理解。
例子0:definitely lost
definitely lost意思是:確信無(wú)疑的內(nèi)存泄漏,缺少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,說(shuō)的很玄乎,還是看例子比較直觀:
#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);
因?yàn)闆](méi)有free(im),因此im對(duì)應(yīng)的內(nèi)存泄漏了;而im對(duì)應(yīng)的內(nèi)存塊里面,有一個(gè)data字段,data字段現(xiàn)在也沒(méi)有被釋放,因此,602,112字節(jié)說(shuō)的是224*224*4*sizeof(float)這么一大塊內(nèi)存是indirectly lost。需要注意的是,definitely lost此時(shí)為24字節(jié),如果調(diào)用了free(im)也就是消除了當(dāng)前的definitely lost,則當(dāng)前的indirectly lost會(huì)升級(jí)為definitely lost。
例子2:possibly lost
找到possibly lost的例子并不很容易。按官方說(shuō)法是,指針指向malloc申請(qǐng)的內(nèi)存,然后指針往后++,再free這個(gè)指針,就得到possibly lost??纯聪旅孢@兩個(gè)例子,按照這個(gè)解釋,其實(shí)分別得到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類型的內(nèi)存泄漏,和definitely lost類型的內(nèi)存泄漏,我覺(jué)得仍然是一個(gè)是全局變量的指針、另一個(gè)是局部變量指針的區(qū)別。來(lái)看例子:
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
其實(shí)這個(gè)例子就是“例子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)
簡(jiǎn)要總結(jié)Memcheck的內(nèi)存泄漏類型
- definitely lost: 局部變量,用了malloc/calloc申請(qǐng)的堆內(nèi)存,沒(méi)有釋放
- indirectly lost: 某個(gè)局部變量結(jié)構(gòu)體對(duì)象本身是從malloc/calloc申請(qǐng)的堆內(nèi)存,沒(méi)有釋放;并且,這個(gè)結(jié)構(gòu)體的某個(gè)成員是個(gè)指針,也是用malloc/calloc從堆上申請(qǐng)的內(nèi)存,也沒(méi)有釋放。此時(shí),這個(gè)成員指向的內(nèi)存,是indirectly lost。如果這個(gè)結(jié)構(gòu)體對(duì)象被釋放了,那么這個(gè)成員指向的內(nèi)存,就是definitely lost。
- possibly lost:全局變量,指針類型,在某個(gè)函數(shù)中用malloc/calloc申請(qǐng)了堆內(nèi)存給它,然后指針往后偏移了一部分,但是仍然處于這塊堆內(nèi)存里面,并且最后free了這個(gè)指針。那么這是possibly lost。
- still reachable:全局變量,指針類型,在某個(gè)函數(shù)中用malloc/calloc申請(qǐng)了堆內(nèi)存給它,并且這個(gè)指針沒(méi)有偏移;但是呢,最終程序沒(méi)有free掉這個(gè)指針。這就是still reachable。
不管是以上哪種類型的內(nèi)存泄漏,最理想最安全的做法都應(yīng)該是,修復(fù)其中每種錯(cuò)誤,確保沒(méi)有內(nèi)存泄漏。然而事實(shí)上也許并不容易,因?yàn)橐坏┠愕某绦蛴昧说谌綆?kù),而這個(gè)第三方庫(kù)有內(nèi)存泄漏,那就需要搞清楚是誰(shuí)寫(xiě)了bug。典型的例子是OpenCV。
OpenCV內(nèi)存泄漏,例子1
測(cè)試環(huán)境: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
編譯運(yùn)行:如果開(kāi)啟--leak-check=yes,輸出內(nèi)容特別多,這里沒(méi)有開(kāi)啟
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源碼中有內(nèi)存泄漏,至于在哪里并不清楚。
嘗試切換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)
看起來(lái),opencv3.4.5相比于opencv2.4.9,減少一些內(nèi)存泄漏呀。
OpenCV內(nèi)存泄漏,例子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)
看到如上輸出,看來(lái)OpenCV的內(nèi)存泄漏問(wèn)題還是沒(méi)有完全解決。
配置Valgrind,忽略O(shè)penCV的內(nèi)存泄漏
不得不說(shuō),自行去翻看Valgrind中配置memcheck的suppression的文檔感覺(jué)很繁瑣...懶人做法是,讓valgrind自己輸出需要supprress的每一個(gè)配置,重定向到文件,然后去掉額外沒(méi)用的打印輸出內(nèi)容,保存為opencv-3.4.5.supp文件,下次給valgrind用,就可以忽略opencv的內(nèi)存泄漏了。
#輸出內(nèi)存錯(cuò)誤檢測(cè)結(jié)果,并夾帶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)
對(duì)應(yīng)的代碼:(沒(méi)錯(cuò),代碼里僅僅是include了opencv,別的啥都沒(méi)做,但是valgrind就是能檢測(cè)到內(nèi)存泄漏):
#include <stdio.h>
#include <stdlib.h>
#include <opencv2/imgproc/imgproc_c.h>
#include <opencv2/highgui/highgui_c.h>
int main()
{
return 0;
}
沒(méi)有配置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)
沒(méi)錯(cuò),possibily lost從1352字節(jié)減少到0字節(jié),總算把一部分內(nèi)存泄漏的鍋甩給OpenCV了!當(dāng)然,still reachable沒(méi)啥變化。。
這里貼一下我導(dǎo)出的opencv的suppression配置文件的內(nèi)容(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.
翻譯為:
定義”內(nèi)存泄漏”的方式不止一種. 特別的, 在程序員間常用的主要有兩種”內(nèi)存泄漏”的定義.
第一種常用的定義是, “內(nèi)存被分配, 隨后沒(méi)有在程序結(jié)束前被釋放”. 但是, 很多程序員(正確地)主張說(shuō)符合這一定義的內(nèi)存泄漏并不會(huì)造成問(wèn)題, 因此并不被認(rèn)為是真正的內(nèi)存泄漏.
“內(nèi)存泄漏”的一種可能更為嚴(yán)格(也更有用)的定義是, “內(nèi)存被分配后, 由于程序失去了指向被分配內(nèi)存塊的指針而無(wú)法被釋放”. 換句話說(shuō), 你無(wú)法釋放沒(méi)有指針指向的內(nèi)存. 所以這樣的內(nèi)存屬于”內(nèi)存泄漏”. Valgrind用的是這一更為嚴(yán)格的定義. 這類泄漏可能產(chǎn)生嚴(yán)重的堆損耗, 特別是在長(zhǎng)期活動(dòng)的進(jìn)程中.
Valgrind的泄漏報(bào)告中”still reachable”分類指的是只滿足第一類定義的內(nèi)存分配. 這些內(nèi)存塊沒(méi)有被釋放, 但他們是可以被釋放的(只要程序員愿意), 因?yàn)槌绦蛉匀槐S兄赶蜻@些內(nèi)存塊的指針.
一般而言, 不必?fù)?dān)心”still reachable”的內(nèi)存塊. 他們不會(huì)帶來(lái)真正的內(nèi)存泄漏會(huì)導(dǎo)致的問(wèn)題. 比如說(shuō), “still reachable”的內(nèi)存塊通常不會(huì)導(dǎo)致堆耗盡. 這是因?yàn)檫@些塊都是單次分配, 程序在整個(gè)生命周期中都保留對(duì)他們的指向. 你當(dāng)然可以梳理整個(gè)程序, 保證這些內(nèi)存塊都被釋放, 但這實(shí)際并沒(méi)什么好處, 因?yàn)椴僮飨到y(tǒng)會(huì)在進(jìn)程結(jié)束后回收進(jìn)程的全部?jī)?nèi)存. 與之相對(duì), 如果真正的內(nèi)存泄漏沒(méi)有被修正, 那么就會(huì)導(dǎo)致一個(gè)進(jìn)程在運(yùn)行足夠長(zhǎng)時(shí)間后耗盡所有內(nèi)存, 或者說(shuō)消耗比它所必需的多得多的內(nèi)存.
看來(lái),still reachable類型的內(nèi)存泄漏,意思是“本該釋放的堆內(nèi)存,并且一直有指針能夠訪問(wèn)這些堆內(nèi)存,但是到程序結(jié)束的時(shí)候,都沒(méi)有釋放掉”。
上面這大段的引用(英文原文),其實(shí)來(lái)在https://stackoverflow.com/questions/3840582/still-reachable-leak-detected-by-valgrind,這個(gè)SO的問(wèn)題回答和評(píng)論中,還有人提到still reachable的一個(gè)特例:FILE指針。沒(méi)錯(cuò),當(dāng)打開(kāi)一個(gè)文件的時(shí)候,用了FILE* fp,無(wú)論是讀取還是寫(xiě)入,如果程序最終沒(méi)有關(guān)閉這個(gè)fp,valgrind會(huì)把它算作“still reachable”。。。。其實(shí)這個(gè)說(shuō)法也不嚴(yán)禁。經(jīng)過(guò)實(shí)驗(yàn)我發(fā)現(xiàn),在缺少fclose(fp)的情況下:
-
如果是
FILE* fp = fopen("in.txt", "r")(讀取文件)- 文件"in.txt"存在,那么會(huì)有still reachable問(wèn)題
- 不存在"in.txt",則沒(méi)有still reachable問(wèn)題
如果是
FILE* fp=fclose("in.txt", "w")(寫(xiě)入文件),則存在still reachable問(wèn)題(內(nèi)存不足的極端情況沒(méi)有測(cè)試,2333)
顯然,上述關(guān)于文件讀寫(xiě)的情況,是因?yàn)槠鋬?nèi)部實(shí)現(xiàn)中有使用緩沖區(qū)來(lái)存儲(chǔ)需要讀取或?qū)懭氲膬?nèi)容,這個(gè)緩沖區(qū)應(yīng)該是從堆上動(dòng)態(tài)分配的內(nèi)存空間,可以參考APUE(《unix環(huán)境高級(jí)編程)上的相關(guān)描述,例如這篇博客:fopen的默認(rèn)緩沖大小和setvbuf 用法。
此外,這個(gè)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".
個(gè)人比較認(rèn)同這個(gè)回答中的解釋,"Still reachable" are leaks assigned to global and static-local variables(Still reachable對(duì)應(yīng)的內(nèi)存泄漏是說(shuō),全局或者static局部變量的指針,對(duì)應(yīng)的內(nèi)存泄漏)。
使用OpenCV官方的suppression配置文件來(lái)忽略內(nèi)存泄漏
搞了半天發(fā)現(xiàn)自己民科了...opencv的github issue中早有人提出了類似“opencv你怎么在valgrind下有內(nèi)存泄漏”的問(wèn)題:OpenCV 3.3.0 memory leak for CLAHE Ubuntu 16.04,官方issue發(fā)言人也早有了回答:
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
}
試試看?測(cè)試代碼,僅僅是include了opencv而不做任何事情的:
#include <stdio.h>
#include <stdlib.h>
#include <opencv2/imgproc/imgproc_c.h>
#include <opencv2/highgui/highgui_c.h>
int main()
{
return 0;
}
結(jié)果輸出:
==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,并不完美。