您的位置:首页 > 其它

window使用内存泄漏检测工具Leakdiag记录

2016-06-05 13:50 711 查看

Leakdiag安装

windows下内存泄漏查找工具Leakdiag使用起来还是蛮方便的。

Leakdiag下载之后按照默认设置安装,否则好像是会出问题的。

默认安装是直接装在C盘下。

那它是如何来检测内存泄漏的呢?

下图所示是Leakdiag目录,Logs文件夹里边存储的就是记录的日志,我们则是通过启动leakdiag应用程序进行检测。



第一次试验

首先测试一下常见的new和malloc作为练手。

在vs里边写了以下程序:

void testNew()
{
char *data = new char[100];
data[0] = 'a';
cout << data[0];
}
void testNewWithDelete()
{
char *data1 = new char[100];
data1[0] = 'b';
cout << data1[0];
delete[] data1;
data1 = NULL;
}
void testMalloc()
{
int *data2 = (int *)malloc(sizeof(int)*100);
data2[0] = 1;
cout << data2[0];
}
void testMallocWithFree()
{
int *data3 = (int *)malloc(sizeof(int)*100);
data3[0] = 2;
cout << data3[0];
free(data3);
data3 = NULL;
}
int main()
{
for (int i = 0; i < 100000; i++)
{
testNew();
testNewWithDelete();
testMalloc();
testMallocWithFree();
}
return 0;
}


我的工程名字叫memoryTest,然后启动调试,会出现memoryTest.exe。

然后启动Leakdiag应用程序,出现下图,我们首先在application里边找到memoryTest.exe,然后在Memory allocators这里选择Heap Allocator,因为内存泄漏主要是因为程序员申请了空间忘记释放,这些内存空间都是在堆区申请,所以查看堆区就好了,然后按下start。



之后到这一幅图,我们点击Log按钮,它就开始记录了,觉得程序已经把我们要检测的部分都跑好了就可以按stop,至此操作结束。



查看Log记录

在Logs目录里边出现了刚刚记录的xml文件,我是用Notepad++来打开xml文件的。



打开xml文件以后,内容好多哦,不过我们只需要关心有

dll=”memoryTest.exe”的部分。

下面按照顺序(在新标签页打开图片会显示更加清楚):



第一个是

<FRAME num="0" dll="MSVCR100.dll" function ="malloc" offset="0x36" filename="" line="" addr="0x713B0269" />
<FRAME num="1" dll="memoryTest.exe" function ="main" offset="0x5B" filename="e:\vsworkspace\memorytest\memorytest\test.cpp" line="81" addr="0x10610BB" />
<FRAME num="2" dll="memoryTest.exe" function ="__tmainCRTStartup" offset="0x122" filename="f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c" line="555" addr="0x1062211" />


首先观察第一个,从下往上看,首先看frame num=2的这一行,提示函数是__tmainCRTStartup,不管它。

然后是frame num = 1的这一行,提示函数是main,说明问题出在main函数里边,接着又line = “81”,说明问题出在第81行。

然后再往上一行,frame num = 0这一行,提示函数是malloc,说明是malloc带来的问题。

第二个:

<FRAME num="0" dll="MSVCR100.dll" function ="malloc" offset="0x36" filename="" line="" addr="0x713B0269" />
<FRAME num="1" dll="MSVCR100.dll" function ="operator new" offset="0x10" filename="" line="" addr="0x713B233B" />
<FRAME num="2" dll="memoryTest.exe" function ="main" offset="0x27" filename="e:\vsworkspace\memorytest\memorytest\test.cpp" line="79" addr="0x1061087" />


这一段便是说在main的79行里边使用了new,而在frame=0这里居然有malloc,说明new的实现也是依靠了malloc哦!

我们回过头来看看main函数,出问题的地方在79行和81行,正好对了是吧!



使用注意

上面示例正确地指出了出现问题的地方,但是使用不当就会出问题的。

代码顺序

首先是代码上的问题,我是按照以下顺序写代码的。

1.申请空间

2.对空间进行赋值

3.使用空间中的某个值

4.释放空间(如果有的话)

经过实验发现如果去掉了第3个步骤,也就是使用空间的值,在我这里便是cout,那么结果就会出问题,不论有没有步骤2,最终结果都如下,提示在80和82行出了问题, ( ´◔ ‸◔`),这样找出的结果就完全搓掉了呢,所以一定要使用了未释放的空间,它才能正确找到位置!赋值不算是使用哦!

<FRAME num="0" dll="MSVCR100.dll" function ="malloc" offset="0x36" filename="" line="" addr="0x71350269" />
<FRAME num="1" dll="MSVCR100.dll" function ="operator new" offset="0x10" filename="" line="" addr="0x7135233B" />
<FRAME num="2" dll="memoryTest.exe" function ="main" offset="0x27" filename="e:\vsworkspace\memorytest\memorytest\test.cpp" line="80" addr="0x361087" />

<FRAME num="0" dll="MSVCR100.dll" function ="malloc" offset="0x36" filename="" line="" addr="0x71350269" />
<FRAME num="1" dll="memoryTest.exe" function ="main" offset="0x3E" filename="e:\vsworkspace\memorytest\memorytest\test.cpp" line="82" addr="0x36109E" />
<FRAME num="2" dll="memoryTest.exe" function ="__tmainCRTStartup" offset="0x122" filename="f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c" line="555" addr="0x361FD1" />


函数里边or直接放在main里边

我在示例里边都是将测试代码放在函数里边,在main函数里边对这些函数进行调用。如果我直接将这些代码放到main函数里边呢?

代码如下:

int main()
{
for (int i = 0; i < 10000000; i++)
{
char *data = new char[100];
//data[0] = 'a';
cout << data[0];
char *data1 = new char[100];
//data1[0] = 'b';
cout << data1[0];
delete[] data1;
data1 = NULL;

int *data2 = (int *)malloc(sizeof(int)*100);
//data2[0] = 1;
cout << data2[0];
int *data3 = (int *)malloc(sizeof(int)*100);
//data3[0] = 2;
cout << data3[0];
free(data3);
data3 = NULL;
}
return 0;
}


得到的记录如下:

<FRAME num="0" dll="MSVCR100.dll" function ="malloc" offset="0x36" filename="" line="" addr="0x71350269" />
<FRAME num="1" dll="memoryTest.exe" function ="main" offset="0x62" filename="e:\vsworkspace\memorytest\memorytest\test.cpp" line="90" addr="0xE910C2" />
<FRAME num="2" dll="memoryTest.exe" function ="__tmainCRTStartup" offset="0x122" filename="f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c" line="555" addr="0xE92211" />
<FRAME num="3" dll="kernel32.dll" function ="BaseThreadInitThunk" offset="0x12" filename="" line="" addr="0x760E1174" />
<FRAME num="4" dll="ntdll.dll" function ="RtlInitializeExceptionChain" offset="0x63" filename="" line="" addr="0x77A7B3F5" />
<STACKID>003B42A8</STACKID>
</STACK>
<STACK numallocs="0753" size="0100" totalsize="075300">
<STACKSTATS>
<SIZESTAT size="0100" numallocs="0753"/>
<HEAPSTAT handle="460000" numallocs="0753"/>
</STACKSTATS>
<FRAME num="0" dll="MSVCR100.dll" function ="malloc" offset="0x36" filename="" line="" addr="0x71350269" />
<FRAME num="1" dll="MSVCR100.dll" function ="operator new" offset="0x10" filename="" line="" addr="0x7135233B" />
<FRAME num="2" dll="memoryTest.exe" function ="main" offset="0x27" filename="e:\vsworkspace\memorytest\memorytest\test.cpp" line="81" addr="0xE91087" />
<FRAME num="3" dll="memoryTest.exe" function ="__tmainCRTStartup" offset="0x122" filename="f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c" line="555" addr="0xE92211" />
<FRAME num="4" dll="kernel32.dll" function ="BaseThreadInitThunk" offset="0x12" filename="" line="" addr="0x760E1174" />


记录显示在第90行出现malloc问题,第81行出现了new问题,正好找对了!



如果我把赋值语句加上,去掉赋值语句的注释,那么提示我在第90行出现malloc问题,第79和82行出现了new问题, (*゜ロ゜)ノ ,这个这个,找多了啊!!我又试了一次,这次按了log之后让它多跑了一段时间,还是这个样子。╮(╯▽╰)╭,这就是main里边跑的问题了,虽然它能更加准确地告诉我们问题发生在哪里,不过经常会误报,不过也不算大问题,误报的地方很容易就能被我们给发现啦。

LeakDiag的使用注意

这里也有一个严格的顺序:

1.启动vs程序

2.启动LeakDiag程序,在里边找到vs启动的这个程序

3.选择Heap Allocator,此时start亮了,点击它

4.过会点击Log按钮

5.稍等片刻点击stop按钮,关掉LeakDiag

6.结束vs程序

如果在点stop之前结束了vs程序,那么stop会按不下去。

如果首先启动LeakDiag,那么会找不到vs启动的程序

注意LeakDiag是按照进程PID来定位程序的,我每次启动vs得到的程序它的PID是不同的,所以必须要在vs启动之后再启动LeakDiag才能找到正确的vs程序。

我遇到过的内存泄漏情况

首先是类里边的:

1.类的动态对象

我简单地定义了如下的类

class MyClass
{
private:
int a;
int b;
map<int, string> myMap;
public:
//一开始只是写出MyClass();没有加{},然后提示我未定义的符号MyClass,确实是没定义哦
MyClass()
{
myMap[0] = "abc";
myMap[2] = "def";
}

~MyClass(){};
void fun()
{
map<int, string>::iterator it;
it = myMap.find(0);
if (it != myMap.end())
{
cout << it->second.c_str() << endl;
}
}

};


之后我测试了如下代码:

MyClass *myclass1 = new MyClass();
myclass1->fun();
//myclass->~MyClass();
delete myclass1;//和其它的是一样的
myclass1 = NULL;


提示我因为function =”operator new”导致内存泄漏。

●▂●什么鬼啊,我明明写了delete的啊,而且delete也会自动调用myclass类的析构函数,可为什么还是会报错呢?这个我真是没想到原因也不知道该怎么解决了,好奇怪啊。不知道是不是误报。

2.类里边的stl-map

依然是上边那个类的测试,提示因为map产生了内容泄漏。

function ="operator new"
function ="std___Tree_val..."
function ="std__map..."


如果直接在函数里边使用map是不会出现这个问题的,但是在类里边使用它就出现了这个问题。

这个问题我找到的解决办法就是在类的析构函数里边加上一句话:

myMap.clear();


如果是在类里边使用vector,那是没有问题的,这应该是因为vector和map申请新空间的方式不同引起的。

类里边的问题暂时就这么多了,下面记录下在opencv里边的问题。

1.IplImage的create

IplImage创建图像如果不主动释放是肯定会内存泄漏的,不过我挺好奇的是它load图像不知道会不会也有问题,所以对以下代码进行了测试。

IplImage* src = cvLoadImage("E:/1.jpg");

IplImage* temp = cvCreateImage( cvSize( 10, 10 ), src->depth, src->nChannels );

IplImage* temp1 = cvCreateImage( cvSize( 10, 10 ), src->depth, src->nChannels );
cvReleaseImage(&temp1);


第一个载入图片没问题,记录中关于load图像有下面这一段记录,从中可以看到这个cvLoadImage函数的调用顺序,最后还是依靠了malloc,不过这一段并没有dll=”memoryTest.exe”,说明在我们的程序里边它是不引起内存泄漏的。



第二句引起了问题,问题描述如下



解决办法便是第三句里边的加上cvReleaseImage。

一般来说使用了IplImage *的图像都要配套使用一个cvReleaseImage,所以我这里的载入图片也应该加上的。

2.Mat参数传递

我对Mat的载入图片,创建图片以及引用参数创建图片进行测试。

void matInFun(Mat &result){

result = Mat(100, 100,CV_8U,cv::Scalar(0));

}
void matInFun2(Mat result){

result = Mat(100, 100,CV_8U,cv::Scalar(0));
}

//测试Mat的创建,作为参数传递时,引用于非引用有区别
Mat matA, matB, matC;
matInFun(matA);
matInFun2(matB);

matInFun(matC);
matC.release();
////测试Mat的读取图片
Mat srcMat = imread("E:/1.jpg");


上面的代码在matInFun(matA)这一句上报错了,具体错误如下:



Mat的载入图片以及普通情况下的创建图片都是没有问题的,不过作为引用参数在函数里边创建图片的时候则会出现问题。

解决办法:使用完用引用创建的Mat之后,调用release()函数即可。

3.HoG特征的使用

HoG特征需要用到HOGDescriptor类,但是这个类申请之后该如何释放呢?

void testImage0()
{
HOGDescriptor *hog=new HOGDescriptor(cvSize(128,32),cvSize(16,16),cvSize(8,8),cvSize(8,8),9);

}
void testImage1()
{
Mat test = imread("E:/1.jpg");
resize(test,test,Size(128,32),0,0,CV_INTER_LINEAR);
HOGDescriptor *hog=new HOGDescriptor(cvSize(128,32),cvSize(16,16),cvSize(8,8),cvSize(8,8),9);
vector<float>descriptors;//结果数组
hog->compute(test, descriptors,Size(1,1), Size(0,0)); //调用计算函数开始计算
cout<<"HOG dims: "<<descriptors.size()<<endl;
hog->~HOGDescriptor();//有这个就不会提示内存错误了

//delete之后记得要赋值为NULL,避免野指针的情况出现
/*delete hog;
hog = NULL;*/
}
void testImageBad()
{
Mat test = imread("E:/1.jpg");
resize(test,test,Size(128,32),0,0,CV_INTER_LINEAR);
HOGDescriptor *hog=new HOGDescriptor(cvSize(128,32),cvSize(16,16),cvSize(8,8),cvSize(8,8),9);
vector<float>descriptors;//结果数组
hog->compute(test, descriptors,Size(1,1), Size(0,0)); //调用计算函数开始计算
cout<<"HOG dims: "<<descriptors.size()<<endl;

delete hog;
hog = NULL;
}


测试结果挺匪夷所思,

如果我是这样测试的

int main()
{
testImage1();
return 0;
}


或者

int main()
{
testImageBad();
return 0;
}


那么都不会报错。

但是如果我是这样写



下面报的错是在83行,也就是testImageBad函数,但是紧接着上一行的解释居然是function =”testImage1”,(#‵′)靠,什么奇怪的结果啊。



所以我觉得最好的解决办法就是把所有的都写一下:

先调用析构,然后delete,最好赋值为NULL,这样就肯定不会出错了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息