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,这样就肯定不会出错了。
相关文章推荐
- 如何重装TCP/IP协议
- Windows 8 官方高清壁纸欣赏与下载
- 谁是桌面王者?Win PK Linux三大镇山之宝
- 对《大家都在点赞 Windows Terminal,我决定给你泼一盆冷水》一文的商榷
- Windows Clang开发环境备忘
- 从Windows系统下访问Linux分区相关软件
- 对《大家都在点赞 Windows Terminal,我决定给你泼一盆冷水》一文的商榷
- Windows下搭建本地SVN服务器
- 使用Windows原生命令一键清空剪贴板
- windows用windeployqt发布qt quick application程序
- 利用开源软件打造自己的全功能远程工具
- Windows 8虚拟机不能全屏的解决方法
- Visual Studio 2012 示例代码浏览器 - 数以千计的开发示例近在手边,唾手可得
- Visual Studio 2012 示例代码浏览器 - 数以千计的开发示例近在手边,唾手可得
- 微软镜像下载
- windows server域用户提升到本地更高权限组中的方法
- 使用命令修改注册表键值及权限