内存泄漏检测工具的原理

Windows&CE&内存泄漏的检测和防止
扫扫二维码,随身浏览文档
手机或平板扫扫即可继续访问
WinCE 内存泄漏的检测和防止
举报该文档为侵权文档。
举报该文档含有违规或不良信息。
反馈该文档无法正常浏览。
举报该文档为重复文档。
推荐理由:
将文档分享至:
分享完整地址
文档地址:
粘贴到BBS或博客
flash地址:
支持嵌入FLASH地址的网站使用
html代码:
&embed src='/DocinViewer-.swf' width='100%' height='600' type=application/x-shockwave-flash ALLOWFULLSCREEN='true' ALLOWSCRIPTACCESS='always'&&/embed&
450px*300px480px*400px650px*490px
支持嵌入HTML代码的网站使用
您的内容已经提交成功
您所提交的内容需要审核后才能发布,请您等待!
3秒自动关闭窗口posts - 48,&
comments - 21,&
trackbacks - 0
编者按: 这几天在调试课本上关于二叉树的有关代码,发现严的这本数据结构教程喜欢把简单问题复杂化,当然这是从学生角度出发,从个人角度出发,这本书注重代码的可读性、重用等,更侧重于与软件工程、软件组织结合,当然是不错的教材。比如,其栈和队列的顺序实现,也都用指针来代替数组,让学生是很难理解。我在实现过程中,也有点拿不稳,特别是用队列或栈来存储树的结点(也是指针)时,为了确保没问题,特别是内存的分配,我搜索并安装了Virtual Leak Detector,一个开源的内存泄漏检测工具。以下是转:初识Visual Leak Detector       灵活自由是C/C++语言的一大特色,而这也为C/C++程 序员出了一个难题。当程序越来越复杂时,内存的管理也会变得越加复杂,稍有不慎就会出现内存问题。内存泄漏是最常见的内存问题之一。内存泄漏如果不是很严 重,在短时间内对程序不会有太大的影响,这也使得内存泄漏问题有很强的隐蔽性,不容易被发现。然而不管内存泄漏多么轻微,当程序长时间运行时,其破坏力是 惊人的,从性能下降到内存耗尽,甚至会影响到其他程序的正常运行。另外内存问题的一个共同特点是,内存问题本身并不会有很明显的现象,当有异常现象出现时 已时过境迁,其现场已非出现问题时的现场了,这给调试内存问题带来了很大的难度。             Visual Leak Detector是一款用于Visual C++的免费的内存泄露检测工具。可以在 下载到。相比较其它的内存泄露检测工具,它在检测到内存泄漏的同时,还具有如下特点:1、可以得到内存泄漏点的调用堆栈,如果可以的话,还可以得到其所在文件及行号;2、可以得到泄露内存的完整数据;3、可以设置内存泄露报告的级别;4、它是一个已经打包的lib,使用时无须编译它的源代码。而对于使用者自己的代码,也只需要做很小的改动;5、他的源代码使用GNU许可发布,并有详尽的文档及注释。对于想深入了解堆内存管理的读者,是一个不错的选择。             可见,从使用角度来讲,Visual Leak Detector简单易用,对于使用者自己的代码,唯一的修改是#include Visual Leak Detector的头文件后正常运行自己的程序,就可以发现内存问题。从研究的角度来讲,如果深入Visual Leak Detector源代码,可以学习到堆内存分配与释放的原理、内存泄漏检测的原理及内存操作的常用技巧等。       本文首先将介绍Visual Leak Detector的使用方法与步骤,然后再和读者一起初步的研究Visual Leak Detector的源代码,去了解Visual Leak Detector的工作原理。使用Visual Leak Detector(1.0)       下面让我们来介绍如何使用这个小巧的工具。       首先从网站上下载zip包,解压之后得到vld.h, vldapi.h, vld.lib, vldmt.lib, vldmtdll.lib, dbghelp.dll等文件。将.h文件拷贝到Visual C++的默认include目录下,将.lib文件拷贝到Visual C++的默认lib目录下,便安装完成了。因为版本问题,如果使用windows 2000或者以前的版本,需要将dbghelp.dll拷贝到你的程序的运行目录下,或其他可以引用到的目录。注:我下载的是较新版1.9,直接安装到系统中。因此使用时必须先在VC中设置一下目录。       接下来需要将其加入到自己的代码中。方法很简单,只要在包含入口函数的.cpp文件中包含vld.h就可以。如果这个cpp文件包含了stdafx.h,则将包含vld.h的语句放在stdafx.h的包含语句之后,否则放在最前面。如下是一个示例程序:#include &vld.h>void main(){…                }       接下来让我们来演示如何使用Visual Leak Detector检测内存泄漏。下面是一个简单的程序,用new分配了一个int大小的堆内存,并没有释放。其申请的内存地址用printf输出到屏幕上。编译运行后,在标准输出窗口得到:p=003a89c0 在Visual C++的Output窗口得到: WARNING: Visual Leak Detector detected memory leaks!---------- Block 57 at 0x003A89C0: 4 bytes ----------  --57号块0x003A89C0地址泄漏了4个字节  Call Stack:                                               --下面是调用堆栈    d:\test\testvldconsole\testvldconsole\main.cpp (7): f  --表示在main.cpp第7行的f()函数    d:\test\testvldconsole\testvldconsole\main.cpp (14): main –双击以引导至对应代码处    f:\rtm\vctools\crt_bld\self_x86\crt\src\crtexe.c (586): __tmainCRTStartup    f:\rtm\vctools\crt_bld\self_x86\crt\src\crtexe.c (403): mainCRTStartup    0x7C816D4F (File and line number not available): RegisterWaitForInputIdle  Data:                                   --这是泄漏内存的内容,0x    78 56 34 12                                                  xV4..... ........ Visual Leak Detector detected 1 memory leak.   第二行表示57号块有4字节的内存泄漏,地址为0x003A89C0,根据程序控制台的输出,可以知道,该地址为指针p。程序的第7行,f()函数里,在该地址处分配了4字节的堆内存空间,并赋值为0x,这样在报告中,我们看到了这4字节同样的内容。可以看出,对于每一个内存泄漏,这个报告列出了它的泄漏点、长度、分配该内存时的调用堆栈、和泄露内存的内容(分别以16进制和文本格式列出)。双击该堆栈报告的某一行,会自动在代码编辑器中跳到其所指文件的对应行。这些信息对于我们查找内存泄露将有很大的帮助。这是一个很方便易用的工具,安装后每次使用时,仅仅需要将它头文件包含进来重新build就可以。而且,该工具仅在build Debug版的时候会连接到你的程序中,如果build Release版,该工具不会对你的程序产生任何性能等方面影响。所以尽可以将其头文件一直包含在你的源代码中。Visual Leak Detector工作原理       下面让我们来看一下该工具的工作原理。       在这之前,我们先来看一下Visual C++内置的内存泄漏检测工具是如何工作的。Visual C++内置的工具CRT Debug Heap工作原来很简单。在使用Debug版的malloc分配内存时,malloc会在内存块的头中记录分配该内存的文件名及行号。当程序退出时CRT会在main()函数返回之后做一些清理工作,这个时候来检查调试堆内存,如果仍然有内存没有被释放,则一定是存在内存泄漏。从这些没有被释放的内存块的头中,就可以获得文件名及行号。       这种静态的方法可以检测出内存泄漏及其泄漏点的文件名和行号,但是并不知道泄漏究竟是如何发生的,并不知道该内存分配语句是如何被执行到的。要想了解这些,就必须要对程序的内存分配过程进行动态跟踪。Visual Leak Detector就是这样做的。它在每次内存分配时将其上下文记录下来,当程序退出时,对于检测到的内存泄漏,查找其记录下来的上下文信息,并将其转换成报告输出。      初始化       Visual Leak Detector要记录每一次的内存分配,而它是如何监视内存分配的呢?Windows提供了分配钩子(allocation hooks)来监视调试堆内存的分配。它是一个用户定义的回调函数,在每次从调试堆分配内存之前被调用。在初始化时,Visual Leak Detector使用_CrtSetAllocHook注册这个钩子函数,这样就可以监视从此之后所有的堆内存分配了。       如何保证在Visual Leak Detector初始化之前没有堆内存分配呢?全局变量是在程序启动时就初始化的,如果将Visual Leak Detector作为一个全局变量,就可以随程序一起启动。但是C/C++并没有约定全局变量之间的初始化顺序,如果其它全局变量的构造函数中有堆内存分配,则可能无法检测到。Visual Leak Detector使用了C/C++提供的#pragma init_seg来在某种程度上减少其它全局变量在其之前初始化的概率。根据#pragma init_seg的定义,全局变量的初始化分三个阶段:首先是compiler段,一般c语言的运行时库在这个时候初始化;然后是lib段,一般用于第三方的类库的初始化等;最后是user段,大部分的初始化都在这个阶段进行。Visual Leak Detector将其初始化设置在compiler段,从而使得它在绝大多数全局变量和几乎所有的用户定义的全局变量之前初始化。 记录内存分配       一个分配钩子函数需要具有如下的形式:int YourAllocHook( int allocType, void *userData, size_t size, int blockType, long requestNumber, const unsigned char*filename, int lineNumber);       就像前面说的,它在Visual Leak Detector初始化时被注册,每次从调试堆分配内存之前被调用。这个函数需要处理的事情是记录下此时的调用堆栈和此次堆内存分配的唯一标识——requestNumber。       得到当前的堆栈的二进制表示并不是一件很复杂的事情,但是因为不同体系结构、不同编译器、不同的函数调用约定所产生的堆栈内容略有不同,要解释堆栈并得到整个函数调用过程略显复杂。不过windows提供一个StackWalk64函数,可以获得堆栈的内容。StackWalk64的声明如下:BOOL StackWalk64(  DWORD MachineType,  HANDLE hProcess,  HANDLE hThread,  LPSTACKFRAME64 StackFrame,  PVOID ContextRecord,  PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,  PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,  PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,  PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress);STACKFRAME64结构表示了堆栈中的一个frame。给出初始的STACKFRAME64,反复调用该函数,便可以得到内存分配点的调用堆栈了。    // Walk the stack.    while (count & _VLD_maxtraceframes) {        count++;        if (!pStackWalk64(architecture, m_process, m_thread, &frame, &context,                          NULL, pSymFunctionTableAccess64, pSymGetModuleBase64, NULL)) {            // Couldn't trace back through any more frames.            break;        }        if (frame.AddrFrame.Offset == 0) {            // End of stack.            break;        }         // Push this frame's program counter onto the provided CallStack.        callstack->push_back((DWORD_PTR)frame.AddrPC.Offset);    }       那么,如何得到初始的STACKFRAME64结构呢?在STACKFRAME64结构中,其他的信息都比较容易获得,而当前的程序计数器(EIP)在x86体系结构中无法通过软件的方法直接读取。Visual Leak Detector使用了一种方法来获得当前的程序计数器。首先,它调用一个函数,则这个函数的返回地址就是当前的程序计数器,而函数的返回地址可以很容易的从堆栈中拿到。下面是Visual Leak Detector获得当前程序计数器的程序:#if defined(_M_IX86) || defined(_M_X64)#pragma auto_inline(off)DWORD_PTR VisualLeakDetector::getprogramcounterx86x64 (){    DWORD_PTR     __asm mov AXREG, [BPREG + SIZEOFPTR] // Get the return address out of the current stack frame    __asm mov [programcounter], AXREG    // Put the return address into the variable we'll return     return }#pragma auto_inline(on)#endif // defined(_M_IX86) || defined(_M_X64)       得到了调用堆栈,自然要记录下来。Visual Leak Detector使用一个类似map的数据结构来记录该信息。这样可以方便的从requestNumber查找到其调用堆栈。分配钩子函数的allocType参数表示此次堆内存分配的类型,包括_HOOK_ALLOC, _HOOK_REALLOC, 和 _HOOK_FREE,下面代码是Visual Leak Detector对各种情况的处理。     switch (type) {    case _HOOK_ALLOC:        visualleakdetector.hookmalloc(request);        break;     case _HOOK_FREE:        visualleakdetector.hookfree(pdata);        break;     case _HOOK_REALLOC:        visualleakdetector.hookrealloc(pdata, request);        break;     default:        visualleakdetector.report("WARNING: Visual Leak Detector: in allochook(): Unhandled allocation type (%d).\n", type);        break;    }这里,hookmalloc()函数得到当前堆栈,并将当前堆栈与requestNumber加入到类似map的数据结构中。hookfree()函数从类似map的数据结构中删除该信息。hookrealloc()函数依次调用了hookfree()和hookmalloc()。 检测内存泄露       前面提到了Visual C++内置的内存泄漏检测工具的工作原理。与该原理相同,因为全局变量以构造的相反顺序析构,在Visual Leak Detector析构时,几乎所有的其他变量都已经析构,此时如果仍然有未释放之堆内存,则必为内存泄漏。       分配的堆内存是通过一个链表来组织的,检查内存泄漏则是检查此链表。但是windows没有提供方法来访问这个链表。Visual Leak Detector使用了一个小技巧来得到它。首先在堆上申请一块临时内存,则该内存的地址可以转换成指向一个_CrtMemBlockHeader结构,在此结构中就可以获得这个链表。代码如下:    char *pheap = new char;    _CrtMemBlockHeader *pheader = pHdr(pheap)->pBlockHeaderNdelete 其中pheader则为链表首指针。 报告生成       前面讲了Visual Leak Detector如何检测、记录内存泄漏及其其调用堆栈。但是如果要这个信息对程序员有用的话,必须转换成可读的形式。Visual Leak Detector使用SymGetLineFromAddr64()及SymFromAddr()生成可读的报告。            // Iterate through each frame in the call stack.            for (frame = 0; frame & callstack->size(); frame++) {                // Try to get the source file and line number associated with                // this program counter address.                if (pSymGetLineFromAddr64(m_process,                   (*callstack)[frame], &displacement, &sourceinfo)) {                    ...                }                 // Try to get the name of the function containing this program                // counter address.                if (pSymFromAddr(m_process, (*callstack)[frame],                    &displacement64, pfunctioninfo)) {                    functionname = pfunctioninfo->N                }                else {                    functionname = "(Function name unavailable)";                }                ...            }       概括讲来,Visual Leak Detector的工作分为3步,首先在初始化注册一个钩子函数;然后在内存分配时该钩子函数被调用以记录下当时的现场;最后检查堆内存分配链表以确定是否存在内存泄漏并将泄漏内存的现场转换成可读的形式输出。有兴趣的读者可以阅读Visual Leak Detector的源代码。 总结       在使用上,Visual Leak Detector简单方便,结果报告一目了然。在原理上,Visual Leak Detector针 对内存泄漏问题的特点,可谓对症下药——内存泄漏不是不容易发现吗?那就每次内存分配是都给记录下来,程序退出时算总账;内存泄漏现象出现时不是已时过境 迁,并非当时泄漏点的现场了吗?那就把现场也记录下来,清清楚楚的告诉使用者那块泄漏的内存就是在如何一个调用过程中泄漏掉的。       Visual Leak Detector是一个简单易用内存泄漏检测工具。现在最新的版本是1.9a,采用了新的检测机制,并在功能上有了很多改进。读者不妨体验一下
阅读(2950)
282930311245678910111213141718192021222324252627282930311234567
阅读排行榜
评论排行榜C++ 内存泄漏检测原理_1_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
评价文档:
C++ 内存泄漏检测原理_1
阅读已结束,如果下载本文需要使用
想免费下载本文?
你可能喜欢您正在使用IE低版浏览器,为了您的IThao123账号安全和更好的产品体验,强烈建议使用更快更安全的浏览器
New和delete的原理---如何在linux下检测内存泄漏
New和delete的原理---如何在linux下检测内存泄漏
当我们在程序中写下 new 和 delete 时,我们实际上调用的是 C++ 语言内置的 new operator 和 delete operator。所谓语言内置就是说我们不能更改其含义,它的功能总是一致的。以 new operator 为例,它总是先分配足够的内存,而后再调用相应的类型的构造函数初始化该内存。而 delete operator 总是先调用该类型的析构函数,而后释放内存(图1)。我们能够施加影响力的事实上就是 new operator 和 delete operator 执行过程中分配和释放内存的方法。
new operator 为分配内存所调用的函数名字是 operator new,其通常的形式是 void * operator new(size_t size); 其返回值类型是 void*,因为这个函数返回一个未经处理(raw)的指针,未初始化的内存。参数 size 确定分配多少内存,你能增加额外的参数重载函数 operator new,但是第一个参数类型必须是 size_t。
delete operator 为释放内存所调用的函数名字是 operator delete,其通常的形式是 void operator delete(void *memoryToBeDeallocated);它释放传入的参数所指向的一片内存区。
这里有一个问题,就是当我们调用 new operator 分配内存时,有一个 size 参数表明需要分配多大的内存。但是当调用 delete operator 时,却没有类似的参数,那么 delete operator 如何能够知道需要释放该指针指向的内存块的大小呢?答案是:对于系统自有的数据类型,语言本身就能区分内存块的大小,而对于自定义数据类型(如我们自定义的类),则 operator new 和 operator delete 之间需要互相传递信息。
当我们使用 operator new 为一个自定义类型对象分配内存时,实际上我们得到的内存要比实际对象的内存大一些,这些内存除了要存储对象数据外,还需要记录这片内存的大小,此方法称为 cookie。这一点上的实现依据不同的编译器不同。(例如 MFC 选择在所分配内存的头部存储对象实际数据,而后面的部分存储边界标志和内存大小信息。g++ 则采用在所分配内存的头 4 个自己存储相关信息,而后面的内存存储对象实际数据。)当我们使用 delete operator 进行内存释放操作时,delete operator
就可以根据这些信息正确的释放指针所指向的内存块。
以上论述的是对于单个对象的内存分配/释放,当我们为数组分配/释放内存时,虽然我们仍然使用 new operator 和 delete operator,但是其内部行为却有不同:new operator 调用了operator new 的数组版的兄弟- operator new[],而后针对每一个数组成员调用构造函数。而 delete operator 先对每一个数组成员调用析构函数,而后调用 operator delete[] 来释放内存。需要注意的是,当我们创建或释放由自定义数据类型所构成的数组时,编译器为了能够标识出在
operator delete[] 中所需释放的内存块的大小,也使用了编译器相关的 cookie 技术。
综上所述,如果我们想检测内存泄漏,就必须对程序中的内存分配和释放情况进行记录和分析,也就是说我们需要重载 operator new/operator new[];operator delete/operator delete[] 四个全局函数,以截获我们所需检验的内存操作信息。
Laravel是一套简洁、优雅的PHP Web开发框架(PHP Web Framework)。它可以让你从面条一样杂乱的代码中解脱出来;它可以帮你构建一个完美的网络APP,而且每行代码都可以简洁、富于表达力。
Hadoop是一个由Apache基金会所开发的分布式系统基础架构。
用户可以在不了解分布式底层细节的情况下,开发分布式程序。充分利用集群的威力进行高速运算和存储。
Hadoop实现了一个分布式文件系统(Hadoop Distributed File System),简称HDFS。HDFS有高容错性的特点,并且设计用来部署在低廉的(low-cost)硬件上;而且它提供高吞吐量(high throughput)来访问应用程序的数据,适合那些有着超大数据集(large data set)的应用程序。HDFS放宽了(relax)POSIX的要求,可以以流的形式访问(streaming access)文件系统中的数据。
Hadoop的框架最核心的设计就是:HDFS和MapReduce。HDFS为海量的数据提供了存储,则MapReduce为海量的数据提供了计算。
产品设计是互联网产品经理的核心能力,一个好的产品经理一定在产品设计方面有扎实的功底,本专题将从互联网产品设计的几个方面谈谈产品设计
随着国内互联网的发展,产品经理岗位需求大幅增加,在国内,从事产品工作的大部分岗位为产品经理,其实现实中,很多从事产品工作的岗位是不能称为产品经理,主要原因是对产品经理的职责不明确,那产品经理的职责有哪些,本专题将详细介绍产品经理的主要职责
IThao123周刊

我要回帖

更多关于 内存泄漏检测原理 的文章

 

随机推荐