c++什么是threadlocal内存泄露露

Linux下几款C++程序中的内存泄露检查工具_linux教程-织梦者
当前位置:&>&&>& > Linux下几款C++程序中的内存泄露检查工具
Linux下几款C++程序中的内存泄露检查工具
Linux下编写C或者C++程序,有很多工具,但是主要编译器仍然是gcc和g++。最近用到STL中的List编程,为了检测写的代码是否会发现内存泄漏,了解了一下相关的知识。
所有使用动态内存分配(dynamic memory allocation)的程序都有机会遇上内存泄露(memory leakage)问题,在Linux里有三种常用工具来检测内存泄露的情況,包括:
参见 http://elinux.org/Memory_Debuggers
一个强大开源的程序检测工具
GNU扩展, 用来跟踪malloc, mtrace为内存分配函数(malloc, realloc, memalign, free)安装hook函数
用于检查C/C++内存泄露(leak)的工具,即检查是否存在直到程序运行结束还没有释放的内存,以一个运行库的方式发布
和dmalloc一样,它能检测未释放的内存、同一段内存被释放多次、位址存取错误及不当使用未分配之内存区域
一个跨平台的 C++ 内存泄漏检测器
Electric Fence
为了方便测试,,我们编写了一个简单的程序, 循环10次每次申请了一个100个字节的单元, 但是却不释放
valgrind介绍
是不是说没有一种内存检查工具能够在Linux使用呢,也不是,像开源的valgrind工具还是相当不错的
Memcheck。这是valgrind应用最广泛的工具,一个重量级的内存检查器,能够发现开发中绝大多数内存错误使用情况,比如:使用未初始化的内存,使用已经释放了的内存,内存访问越界等。这也是本文将重点介绍的部分。
Callgrind。它主要用来检查程序中函数调用过程中出现的问题。
Cachegrind。它主要用来检查程序中缓存使用出现的问题。
Helgrind。它主要用来检查多线程程序中出现的竞争问题。
Massif。它主要用来检查程序中堆栈使用中出现的问题。
Extension。可以利用core提供的功能,自己编写特定的内存调试工具
Valgrind简单用法
Unix下C程序内存泄漏检测工具Valgrind安装与使用
valgrind 的使用简介
应用 Valgrind 发现 Linux 程序的内存问题
如何使用Valgrind memcheck工具进行C/C++的内存泄漏检测
到下载地址)下载最新版的valgrind,按照里面的README提示,安装后就可以使用这个工具来检测内存泄露和内存越界等
/configure
sudo make install
当然也可以使用发行版的自带的源中安装
sudo apt install valgrind
这是一个没有界面的内存检测工具,安装后,输入 valgrind ls -l 验证一下该工具是否工作正常(这是README里面的方法,实际上是验证一下对ls -l命令的内存检测),如果你看到一堆的信息说明你的工具可以使用了。
Valgrind工具包包含多个工具,如Memcheck,Cachegrind,Helgrind, Callgrind,Massif。
最常用的工具,用来检测程序中出现的内存问题,所有对内存的读写都会被检测到,一切对malloc()/free()/new/delete的调用都会被捕获。所以,Memcheck 工具主要检查下面的程序错误
使用未初始化的内存
Use of uninitialised memory
使用已经释放了的内存
Reading/writing memory after it has been free’d
使用超过 malloc分配的内存空间
Reading/writing off the end of malloc’d blocks
对堆栈的非法访问
Reading/writing inappropriate areas on the stack
申请的空间是否有释放
Memory leaks – where pointers to malloc’d blocks are lost forever
malloc/free/new/delete申请和释放内存的匹配
Mismatched use of malloc/new/new [] vs free/delete/delete []
src和dst的重叠
Overlapping src and dst pointers in memcpy() and related functions
这些问题往往是C/C++程序员最头疼的问题,Memcheck在这里帮上了大忙。
和gprof类似的分析工具,但它对程序的运行观察更是入微,能给我们提供更多的信息。和gprof不同,它不需要在编译源代码时附加特殊选项,但加上调试选项是推荐的。Callgrind收集程序运行时的一些数据,建立函数调用关系图,还可以有选择地进行cache模拟。在运行结束时,它会把分析数据写入一个文件。callgrind_annotate可以把这个文件的内容转化成可读的形式。
Cachegrind
Cache分析器,它模拟CPU中的一级缓存I1,Dl和二级缓存,能够精确地指出程序中cache的丢失和命中。如果需要,它还能够为我们提供cache丢失次数,内存引用次数,以及每行代码,每个函数,每个模块,整个程序产生的指令数。这对优化程序有很大的帮助。
它主要用来检查多线程程序中出现的竞争问题。Helgrind寻找内存中被多个线程访问,而又没有一贯加锁的区域,这些区域往往是线程之间失去同步的地方,而且会导致难以发掘的错误。Helgrind实现了名为“Eraser”的竞争检测算法,并做了进一步改进,减少了报告错误的次数。不过,Helgrind仍然处于实验阶段。
堆栈分析器,它能测量程序在堆栈中使用了多少内存,告诉我们堆块,堆管理块和栈的大小。Massif能帮助我们减少内存的使用,在带有虚拟内存的现代系统中,它还能够加速我们程序的运行,减少程序停留在交换区中的几率。
此外,lackey和nulgrind也会提供。Lackey是小型工具,很少用到;Nulgrind只是为开发者展示如何创建一个工具
Valgrind不检查静态分配数组的使用情况
Valgrind占用了更多的内存–可达两倍于你程序的正常使用量
如果你用Valgrind来检测使用大量内存的程序就会遇到问题,它可能会用很长的时间来运行测试
g++ -g -o leak leak.c
被检测程序加入 –g
-fno-inline 编译选项保留调试信息, 否则后面的valgrind不能显示到出错行号。
valgrind被设计成非侵入式的,它直接工作于可执行文件上,因此在检查前不需要重新编译、连接和修改你的程序。要检查一个程序很简单,只需要执行下面的命令就可以了。
valgrind --tool=tool_name program_name
比如我们要对ls -l命令做内存检查,只需要执行下面的命令就可以了
valgrind --tool=memcheck ls -l
如果不知道有哪些参数, 可以先输入valgrind –tool=, 然后狂按两次tab, 会输出linux系统的只能提示, 同样,如果你输入了valgrind –tool=mem再狂按两次tab,linux系统会为你自动补全
使用valgrind检测Memcheck
下面我们就可以用valgrind对我们的程序检测leak
valgrind --tool=memcheck --leak-check=full --show-reachable=yes --trace-children=yes
其中–leak-check=full 指的是完全检查内存泄漏,
–show-reachable=yes是显示内存泄漏的地点,
–trace-children=yes是跟入子进程。
当程序正常退出的时候valgrind自然会输出内存泄漏的信息原理:
mtrace检测内存泄露
mtrace简介
在一般的linux发行版中,有一个自带的工具可以很方便的替你完成这些事,这个工具就是mtrace.
mtrace其实是GNU扩展函数,用来跟踪malloc。
mtrace为内存分配函数(malloc, realloc, memalign, free)安装hook函数。这些hook函数记录内存的申请和释放的trace信息。
在程序中,这些trace信息可以被用来发现内存泄漏和释放不是申请的内存。
当调用mtrace,mtrace会检查环境变量MALLOC_TRACE。该环境变量应该包含记录trace信息的文件路径。如果文件可以被成功打开,它的大小被截断为0。
如果MALLOC_TRACE没有设置,或者设置的文件不可用或者不可写,那么将不会安装hook函数,mtrace不生效。
详细说明可参考man page:man 3 mtrace
mtrace使用
mtrace能监测程序是否内存泄露
在程序的起始处包含头文件
#include &mcheck.h&
更改环境变量:export
MALLOC_TRACE=”mtrace.out”可以加入如下代码
setenv("MALLOC_TRACE", "mtrace.out", 1);
调用函数mtrace()
编译程序带上 -g 选项
gcc -g -c leak_mtrace.c -o leak_mtrace.o
-std=gnu9x -Wall
运行程序一次,尽量调用所有程序内的函数。这时调试信息就已经被写入我们指定的mtrace.out文件中
./leak_mtrace
mtrace a.out
mtrace.out查看内存监测情况
mtrace a.out
mtrace.out
dmalloc是一种用于检查C/C++内存泄露(leak)的工具,即检查是否存在直到程序运行结束还没有释放的内存,并且能够精确指出在哪个源文件的第几行。
以上就是Linux下几款C++程序中的内存泄露检查工具的全文介绍,希望对您学习和使用linux系统开发有所帮助.
这些内容可能对你也有帮助
更多可查看linux教程列表页。
猜您也会喜欢这些文章Android中常见的内存泄漏和解决方案-CocoaChina_让移动开发更简单
(C)2015 Chukong Technologies,Inc.浅谈C/C++内存泄漏及其检测工具
有了下面这个浅谈C/C++内存泄漏及其检测工具教程,不懂浅谈C/C++内存泄漏及其检测工具的也能装懂了,赶紧get起来装逼一下吧!
  对于一个c/c++程序员来说,泄漏是一个常见的也是令人的问题。已经有许多技术被研究出来以应对这个问题,比如Smart Pointer,Garbage Collection等。Smart Pointer技术比较成熟,STL中已经包含支持Smart Pointer的class,但是它的使用似乎并不广泛,而且它也不能解决所有的问题;Garbage Collection技术在Java中已经比较成熟,但是在c/c++领域的发展并不顺畅,虽然很早就有人思考在C++中也加入GC的支持。现实世界就是这样的,作为一个c/c++程序员,内存泄漏是你心中永远的痛。不过好在现在有许多工具能够帮助我们验证内存泄漏的存在,找出发生问题的代码。    内存泄漏的定义     一般我们常说的内存泄漏是指堆内存的泄漏。堆内存是指程序从堆中分配的,大小任意的(内存块的大小可以在程序运行期决定),使用完后必须显示释放的内存。应用程序一般使用malloc,realloc,new等函数从堆中分配到一块内存,使用完后,程序必须负责相应的调用free或delete释放该内存块,否则,这块内存就不能被再次使用,我们就说这块内存泄漏了。以下这段小程序演示了堆内存发生泄漏的情形:    void MyFunction(int nSize)  {   char* p= new char[nSize];   if( !GetStringFrom( p, nSize ) ){  MessageBox(“Error”);     }   …//using the     }  例一    当函数GetStringFrom()返回零的时候,指针p指向的内存就不会被释放。这是一种常见的发生内存泄漏的情形。程序在入口处分配内存,在出口处释放内存,但是c函数可以在任何地方退出,所以一旦有某个出口处没有释放应该释放的内存,就会发生内存泄漏。    广义的说,内存泄漏不仅仅包含堆内存的泄漏,还包含系统资源的泄漏(resource leak),比如核心态HANDLE,GDI Object,SOCKET, Interface等,从根本上说这些由分配的对象也消耗内存,假如这些对象发生泄漏最终也会导致内存的泄漏。而且,某些对象消耗的是核心态内存,这些对象严重泄漏时会导致整个操作系统不稳定。所以相比之下,系统资源的泄漏比堆内存的泄漏更为严重。    GDI Object的泄漏是一种常见的资源泄漏:    void CMyView::OnPaint( CDC* pDC )  {   CB   CBitmap* pOldB   bmp.LoadBitmap(IDB_MYBMP);   pOldBmp = pDC-SelectObject( &bmp );   …   if( Something() ){     }   pDC-SelectObject( pOldBmp );     }  例二    当函数Something()返回非零的时候,程序在退出前没有把pOldBmp选回pDC中,这会导致pOldBmp指向的HBITMAP对象发生泄漏。这个程序假如长时间的运行,可能会导致整个系统花屏。这种问题在Win9x下比较轻易暴露出来,因为Win9x的GDI堆比Win2k或NT的要小很多。    内存泄漏的发生方式:    以发生的方式来分类,内存泄漏可以分为4类:    1. 常发性内存泄漏。发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。比如例二,假如Something()函数一直返回True,那么pOldBmp指向的HBITMAP对象总是发生泄漏。    2. 偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。比如例二,假如Something()函数只有在特定环境下才返回True,那么pOldBmp指向的HBITMAP对象并不总是发生泄漏。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。    3. 一次性内存泄漏。发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。比如,在类的构造函数中分配内存,在析构函数中却没有释放该内存,但是因为这个类是一个Singleton,所以内存泄漏只会发生一次。另一个例子:    char* g_lpszFileName = NULL;    void SetFileName( const char* lpcszFileName )  {   if( g_lpszFileName ){  free( g_lpszFileName );   }   g_lpszFileName = strdup( lpcszFileName );  }  例三    假如程序在结束的时候没有释放g_lpszFileName指向的字符串,那么,即使多次调用SetFileName(),总会有一块内存,而且仅有一块内存发生泄漏。    4. 隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。举一个例子:     class Connection  {   public:  Connection( SOCKET s);  ~Connection();  …   private:  SOCKET _  …  };    class ConnectionManager  {   public:    
  ConnectionManager(){}  ~ConnectionManager(){   list::   for( it = _connlist.begin(); it != _connlist.end(); ++it ){  delete (*it);   }   _connlist.clear();  }  void OnClientConnected( SOCKET s ){   Connection* p = new Connection(s);   _connlist.push_back(p);  }  void OnClientDisconnected( Connection* pconn ){   _connlist.remove( pconn );     }   private:  list _  };  例四    假设在Client从端断开后,Server并没有呼叫OnClientDisconnected()函数,那么代表那次连接的Connection对象就不会被及时的删除(在Server程序退出的时候,所有Connection对象会在ConnectionManager的析构函数里被删除)。当不断的有连接建立、断开时隐式内存泄漏就发生了。    从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,作为一般的用户,根本感觉不到内存泄漏的存在。真正有危害的是内存泄漏的堆积,这会最终消耗尽系统所有的内存。从这个角度来说,一次性内存泄漏并没有什么危害,因为它不会堆积,而隐式内存泄漏危害性则非常大,因为较之于常发性和偶发性内存泄漏它更难被检测到。 Photoshop教程 数据结构 五笔输入法专题 QQ病毒专题 共享上网专题 Google工具和服务专题 检测内存泄漏    检测内存泄漏的要害是要能截获住对分配内存和释放内存的函数的调用。截获住这两个函数,我们就能跟踪每一块内存的生命周期,比如,每当成功的分配一块内存后,就把它的指针加入一个全局的list中;每当释放一块内存,再把它的指针从list中删除。这样,当程序结束的时候,list中剩余的指针就是指向那些没有被释放的内存。这里只是简单的描述了检测内存泄漏的基本原理,具体的算法可以参见Steve Maguire的Writing Solid Code。    假如要检测堆内存的泄漏,那么需要截获住malloc/realloc/free和new/delete就可以了(其实new/delete最终也是用malloc/free的,所以只要截获前面一组即可)。对于其他的泄漏,可以采用类似的方法,截获住相应的分配和释放函数。比如,要检测BSTR的泄漏,就需要截获SysAllocString/SysFreeString;要检测HMENU的泄漏,就需要截获CreateMenu/ DestroyMenu。(有的资源的分配函数有多个,释放函数只有一个,比如,SysAllocStringLen也可以用来分配BSTR,这时就需要截获多个分配函数)    在Windows平台下,检测内存泄漏的工具常用的一般有三种,MS C-Runtime Library内建的检测功能;外挂式的检测工具,诸如,Purify,BoundsChecker等;利用Windows NT自带的Performance Monitor。这三种工具各有优缺点,MS C-Runtime Library虽然功能上较之外挂式的工具要弱,但是它是免费的;Performance Monitor虽然无法标示出发生问题的代码,但是它能检测出隐式的内存泄漏的存在,这是其他两类工具无能为力的地方。    以下我们具体讨论这三种检测工具:    VC下内存泄漏的检测方法    用MFC开发的应用程序,在DEBUG版模式下编译后,都会自动加入内存泄漏的检测代码。在程序结束后,假如发生了内存泄漏,在Debug窗口中会显示出所有发生泄漏的内存块的信息,以下两行显示了一块被泄漏的内存块的信息:    E:TestMemLeakTestDlg.cpp(70) : {59} normal block at 0x0 bytes long.    Data: abcdefghijklmnop 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70    第一行显示该内存块由TestDlg.cpp文件,第70行代码分配,地址在0x,大小为200字节,{59}是指调用内存分配函数的Request Order,关于它的具体信息可以参见MSDN中_CrtSetBreakAlloc()的帮助。第二行显示该内存块前16个字节的内容,尖括号内是以ASCII方式显示,接着的是以16方式显示。    
  一般大家都误以为这些内存泄漏的检测功能是由MFC提供的,其实不然。MFC只是封装和利用了MS C-Runtime Library的Debug Function。非MFC程序也可以利用MS C-Runtime Library的Debug Function加入内存泄漏的检测功能。MS C-Runtime Library在实现malloc/free,strdup等函数时已经内建了内存泄漏的检测功能。    注重观察一下由MFC Application Wizard生成的项目,在每一个cpp文件的头部都有这样一段宏定义:    #ifdef _DEBUG  #define new DEBUG_NEW  #undef THIS_FILE  static char THIS_FILE[] = __FILE__;  #endif  有了这样的定义,在编译DEBUG版时,出现在这个cpp文件中的所有new都被替换成DEBUG_NEW了。那么DEBUG_NEW是什么呢?DEBUG_NEW也是一个宏,以下摘自afx.h,1632行    #define DEBUG_NEW new(THIS_FILE, __LINE__)  所以假如有这样一行代码:    char* p = new char[200];  经过宏替换就变成了:    char* p = new( THIS_FILE, __LINE__)char[200];  根据C++的标准,对于以上的new的使用方法,编译器会去找这样定义的operator new:    void* operator new(size_t, LPCSTR, int)  我们在afxmem.cpp 63行找到了一个这样的operator new 的实现    void* AFX_CDECL operator new(size_t nSize, LPCSTR lpszFileName, int nLine)  {   return ::operator new(nSize, _NORMAL_BLOCK, lpszFileName, nLine);  }    void* __cdecl operator new(size_t nSize, int nType, LPCSTR lpszFileName, int nLine)  {   …   pResult = _malloc_dbg(nSize, nType, lpszFileName, nLine);   if (pResult != NULL)  return pR   …  }  第二个operator new函数比较长,为了简单期间,我只摘录了部分。很显然最后的内存分配还是通过_malloc_dbg函数实现的,这个函数属于MS C-Runtime Library 的Debug Function。这个函数不但要求传入内存的大小,另外还有文件名和行号两个参数。文件名和行号就是用来记录此次分配是由哪一段代码造成的。假如这块内存在程序结束之前没有被释放,那么这些信息就会输出到Debug窗口里。    这里顺便提一下THIS_FILE,__FILE和__LINE__。__FILE__和__LINE__都是编译器定义的宏。当碰到__FILE__时,编译器会把__FILE__替换成一个字符串,这个字符串就是当前在编译的文件的路径名。当碰到__LINE__时,编译器会把__LINE__替换成一个数字,这个数字就是当前这行代码的行号。在DEBUG_NEW的定义中没有直接使用__FILE__,而是用了THIS_FILE,其目的是为了减小目标文件的大小。假设在某个cpp文件中有100处使用了new,假如直接使用__FILE__,那编译器会产生100个常量字符串,这100个字符串都是飧?/SPANcpp文件的路径名,显然十分冗余。假如使用THIS_FILE,编译器只会产生一个常量字符串,那100处new的调用使用的都是指向常量字符串的指针。    再次观察一下由MFC Application Wizard生成的项目,我们会发现在cpp文件中只对new做了映射,假如你在程序中直接使用malloc函数分配内存,调用malloc的文件名和行号是不会被记录下来的。假如这块内存发生了泄漏,MS C-Runtime Library仍然能检测到,但是当输出这块内存块的信息,不会包含分配它的的文件名和行号。    要在非MFC程序中打开内存泄漏的检测功能非常轻易,你只要在程序的入口处加入以下几行代码:    int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );    tmpFlag = _CRTDBG_LEAK_CHECK_DF;    _CrtSetDbgFlag( tmpFlag );  这样,在程序结束的时候,也就是winmain,main或dllmain函数返回之后,假如还有内存块没有释放,它们的信息会被打印到Debug窗口里。    假如你试着创建了一个非MFC应用程序,而且在程序的入口处加入了以上代码,并且故意在程序中不释放某些内存块,你会在Debug窗口里看到以下的信息:    {47} normal block at 0x00C91C90, 200 bytes long.    Data:
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F  内存泄漏的确检测到了,但是和上面MFC程序的例子相比,缺少了文件名和行号。对于一个比较大的程序,没有这些信息,解决问题将变得十分困难。    为了能够知道泄漏的内存块是在哪里分配的,你需要实现类似MFC的映射功能,把new,maolloc等函数映射到_malloc_dbg函数上。这里我不再赘述,你可以参考MFC的源代码。    由于Debug Function实现在MS C-RuntimeLibrary中,所以它只能检测到堆内存的泄漏,而且只限于malloc,realloc或strdup等分配的内存,而那些系统资源,比如HANDLE,GDI Object,或是不通过C-Runtime Library分配的内存,比如VARIANT,BSTR的泄漏,它是无法检测到的,这是这种检测法的一个重大的局限性。另外,为了能记录内存块是在哪里分配的,源代码必须相应的配合,这在调试一些老的程序非常麻烦,究竟修改源代码不是一件省心的事,这是这种检测法的另一个局限性。    对于开发一个大型的程序,MS C-Runtime Library提供的检测功能是远远不够的。接下来我们就看看外挂式的检测工具。我用的比较多的是BoundsChecker,一则因为它的功能比较全面,更重要的是它的稳定性。这类工具假如不稳定,反而会忙里添乱。到底是出自鼎鼎大名的NuMega,我用下来基本上没有什么大问题。 Photoshop教程 数据结构 五笔输入法专题 QQ病毒专题 共享上网专题 Google工具和服务专题     
  使用BoundsChecker检测内存泄漏:    BoundsChecker采用一种被称为 Code Injection的技术,来截获对分配内存和释放内存的函数的调用。简单地说,当你的程序开始运行时,BoundsChecker的DLL被自动载入进程的地址空间(这可以通过system-level的Hook实现),然后它会修改进程中对内存分配和释放的函数调用,让这些调用首先转入它的代码,然后再执行原来的代码。BoundsChecker在做这些动作的时,无须修改被调试程序的源代码或工程配置文件,这使得使用它非常的简便、直接。    这里我们以malloc函数为例,截获其他的函数方法与此类似。    需要被截获的函数可能在DLL中,也可能在程序的代码里。比如,假如静态连结C-Runtime Library,那么malloc函数的代码会被连结到程序里。为了截获住对这类函数的调用,BoundsChecker会动态修改这些函数的指令。    以下两段汇编代码,一段没有BoundsChecker介入,另一段则有BoundsChecker的介入:    126: _CRTIMP void * __cdecl malloc (  127: size_t nSize  128: )  129: {    00403C10 push ebp  00403C11 mov ebp,esp  130: return _nh_malloc_dbg(nSize, _newmode, _NORMAL_BLOCK, NULL, 0);  00403C13 push 0  00403C15 push 0  00403C17 push 1  00403C19 mov eax,[__newmode (0042376c)]  00403C1E push eax  00403C1F mov ecx,dWord ptr [nSize]  00403C22 push ecx  00403C23 call _nh_malloc_dbg (00403c80)  00403C28 add esp,14h  131: }  以下这一段代码有BoundsChecker介入:    126: _CRTIMP void * __cdecl malloc (  127: size_t nSize  128: )  129: {    00403C10 jmp 01F41EC8  00403C15 push 0  00403C17 push 1  00403C19 mov eax,[__newmode (0042376c)]  00403C1E push eax  00403C1F mov ecx,dword ptr [nSize]  00403C22 push ecx  00403C23 call _nh_malloc_dbg (00403c80)  00403C28 add esp,14h  131: }  当BoundsChecker介入后,函数malloc的前三条汇编指令被替换成一条jmp指令,原来的三条指令被搬到地址01F41EC8处了。当程序进入malloc后先jmp到01F41EC8,执行原来的三条指令,然后就是BoundsChecker的天下了。大致上它会先记录函数的返回地址(函数的返回地址在stack上,所以很轻易修改),然后把返回地址指向属于BoundsChecker的代码,接着跳到malloc函数原来的指令,也就是在00403c15的地方。当malloc函数结束的时候,由于返回地址被修改,它会返回到BoundsChecker的代码中,此时BoundsChecker会记录由malloc分配的内存的指针,然后再跳转到到原来的返回地址去。    假如内存分配/释放函数在DLL中,BoundsChecker则采用另一种方法来截获对这些函数的调用。BoundsChecker通过修改程序的DLL Import Table让table中的函数地址指向自己的地址,以达到截获的目的。    截获住这些分配和释放函数,BoundsChecker就能记录被分配的内存或资源的生命周期。接下来的问题是如何与源代码相关,也就是说当BoundsChecker检测到内存泄漏,它如何报告这块内存块是哪段代码分配的。答案是调试信息(Debug Information)。当我们编译一个Debug版的程序时,编译器会把源代码和代码之间的对应关系记录下来,放到一个单独的文件里(.pdb)或者直接连结进目标程序,通过直接读取调试信息就能得到分配某块内存的源代码在哪个文件,哪一行上。使用Code Injection和Debug Information,使BoundsChecker不但能记录呼叫分配函数的源代码的位置,而且还能记录分配时的Call Stack,以及Call Stack上的函数的源代码位置。这在使用像MFC这样的类库时非常有用,以下我用一个例子来说明:      void ShowXItemMenu()  {   …   CM     menu.CreatePopupMenu();   //add menu items.   menu.TrackPropupMenu();   …  }    void ShowYItemMenu( )  {   …   CM   menu.CreatePopupMenu();   //add menu items.   menu.TrackPropupMenu();   menu.Detach();//this will cause HMENU leak   …  }    BOOL CMenu::CreatePopupMenu()  {   …   hMenu = CreatePopupMenu();   …  }  当调用ShowYItemMenu()时,我们故意造成HMENU的泄漏。但是,对于BoundsChecker来说被泄漏的HMENU是在class CMenu::CreatePopupMenu()中分配的。假设的你的程序有许多地方使用了CMenu的CreatePopupMenu()函数,如CMenu::CreatePopupMenu()造成的,你依然无法确认问题的根结到底在哪里,在ShowXItemMenu()中还是在ShowYItemMenu()中,或者还有其它的地方也使用了CreatePopupMenu()?有了Call Stack的信息,问题就轻易了。BoundsChecker会如下报告泄漏的HMENU的信息:    
  Function  File  Line    CMenu::CreatePopupMenu  E:8168vc98mfcmfcincludeafxwin1.inl  1009    ShowYItemMenu  E:estmemleakmytest.cpp  100  这里省略了其他的函数调用    如此,我们很轻易找到发生问题的函数是ShowYItemMenu()。当使用MFC之类的类库编程时,大部分的API调用都被封装在类库的class里,有了Call Stack信息,我们就可以非常轻易的追踪到真正发生泄漏的代码。    记录Call Stack信息会使程序的运行变得非常慢,因此默认情况下BoundsChecker不会记录Call Stack信息。可以按照以下的步骤打开记录Call Stack信息的选项开关:    1. 打开菜单:BoundsCheckerSetting…     2. 在Error Detection页中,在Error Detection Scheme的List中选择Custom    3. 在Category的Combox中选择 Pointer and leak error check    4. 钩上Report Call Stack复选框    5. 点击Ok    基于Code Injection,BoundsChecker还提供了API Parameter的校验功能,memory over run等功能。这些功能对于程序的开发都非常有益。由于这些内容不属于本文的主题,所以不在此详述了。    尽管BoundsChecker的功能如此强大,但是面对隐式内存泄漏仍然显得苍白。所以接下来我们看看如何用Performance Monitor检测内存泄漏。    使用Performance Monitor检测内存泄漏    NT的内核在过程中已经加入了系统监视功能,比如CPU的使用率,内存的使用情况,I/O操作的频繁度等都作为一个个Counter,应用程序可以通过读取这些Counter了解整个系统的或者某个进程的运行状况。Performance Monitor就是这样一个应用程序。    为了检测内存泄漏,我们一般可以监视Process对象的Handle Count,Virutal Bytes 和Working Set三个Counter。Handle Count记录了进程当前打开的HANDLE的个数,监视这个Counter有助于我们发现程序是否有Handle泄漏;Virtual Bytes记录了该进程当前在虚地址空间上使用的的大小,NT的内存分配采用了两步走的方法,首先,在虚地址空间上保留一段空间,这时操作系统并没有分配物理内存,只是保留了一段地址。然后,再提交这段空间,这时操作系统才会分配物理内存。所以,Virtual Bytes一般总大于程序的Working Set。监视Virutal Bytes可以帮助我们发现一些系统底层的问题; Working Set记录了操作系统为进程已提交的内存的总量,这个值和程序申请的内存总量存在密切的关系,假如程序存在内存的泄漏这个值会持续增加,但是Virtual Bytes却是跳跃式增加的。    监视这些Counter可以让我们了解进程使用内存的情况,假如发生了泄漏,即使是隐式内存泄漏,这些Counter的值也会持续增加。但是,我们知道有问题却不知道哪里有问题,所以一般使用Performance Monitor来验证是否有内存泄漏,而使用BoundsChecker来找到和解决。    当Performance Monitor显示有内存泄漏,而BoundsChecker却无法检测到,这时有两种可能:第一种,发生了偶发性内存泄漏。这时你要确保使用Performance Monitor和使用BoundsChecker时,程序的运行环境和操作方法是一致的。第二种,发生了隐式的内存泄漏。这时你要重新审查程序的设计,然后仔细研究Performance Monitor记录的Counter的值的变化图,分析其中的变化和程序运行逻辑的关系,找到一些可能的原因。这是一个痛苦的过程,布满了假设、猜想、验证、失败,但这也是一个积累经验的绝好机会。    总结    内存泄漏是个大而复杂的问题,即使是Java和.Net这样有Gabarge Collection机制的环境,也存在着泄漏的可能,比如隐式内存泄漏。由于篇幅和能力的限制,本文只能对这个主题做一个粗浅的研究。其他的问题,比如多模块下的泄漏检测,如何在程序运行时对内存使用情况进行分析等等,都是可以深入研究的题目。假如您有什么想法,建议或发现了某些错误,欢迎和我交流。
来源:https://www.tulaoshi.com/n/6381.html
点击查看更多与《》相关的文章>>
下面这个在C++中侦测内嵌型别的存在教程由图老师小编精心推荐选出,过程简单易学超容易上手,喜欢就要赶紧get起来哦!
图老师设计创意栏目是一个分享最好最实用的教程的社区,我们拥有最用心的各种教程,今天就给大家分享基于C++执行内存memcpy效率测试的分析的教程,热爱PS的朋友们快点看过来吧!
今天图老师小编给大家精心推荐个C++内存管理详解教程,一起来看看过程究竟如何进行吧!喜欢还请点个赞哦~
最近很多朋友喜欢上设计,但是大家却不知道如何去做,别担心有图老师给你解答,史上最全最棒的详细解说让你一看就懂。
下面图老师小编跟大家分享浅谈C++函数的参数,一起来学习下过程究竟如何进行吧!喜欢就赶紧收藏起来哦~
今天天气好晴朗处处好风光,好天气好开始,图老师又来和大家分享啦。下面给大家推荐水滴石穿C语言之代码检查工具,希望大家看完后也有个好心情,快快行动吧!
热门搜索:
ps图片教程 让逆光人像彰显自然
防火墙在哪里设置 怎样关闭防火墙
如何在WPS演示中给文字添加陀螺旋效果
使用PS制作QQ个性签名图片
羽毛球拍是常用的运动器材之一,有球柄球杆及击球面组成,击球面为网状,球杆细直。通过学习羽毛球拍简笔画知晓更多运动知识。接下来就通过学习羽毛球拍简笔画教程来了解如何绘画羽毛球拍简笔画。
羽毛球是常见的运动器材,由羽毛和海绵托组成,在羽毛的排列绘画上需要一定的技巧方能画的自然。通过羽毛球简笔画绘画学习还可以了解更多相关知识。接下来就让我们一起来学如何画羽毛球简笔画吧!
橄榄球是美国的国粹运动,其形状为椭圆形,纹理类似篮球,绘画难度不高。通过学习绘画橄榄球简笔画我们还可以了解更多橄榄球历史及相关知识。接下来就让我们一起来学习如何绘画橄榄球简笔画吧!
我们现在很多人都非常喜欢饲养猫咪作为宠物,因为猫咪确实是一种非常可爱的小动物。但是很多人觉得猫咪非常小,身体又非常的软,所以总觉得猫咪需要多多的补充蛋白质。如何给猫咪补充蛋白质?
怎么利用Photoshop给电商模特照片精修磨皮?很多朋友在工作中经常会遇到要求精修模特的情况,特别是做化妆品行业的电商设计师,所以总结了一下我的模特修图方法和技巧与大家分享,喜欢的朋友让我们一起来学习吧。
磨皮是我们ps照片最基本的技能,Photoshop人物磨皮是我们必须掌握的,如今不少国人对Photoshop修图的三大酷炫神技双曲线、中性灰与高低频几近痴迷,抓耳挠腮想学会它,下面一起看看教程吧。
掌握一点Photoshop教程对你是非常有用的,这篇教程是给大家分享PS简单快速创建眼睛彩虹美瞳效果方法,教程最终创建出来的效果非常漂亮,而且难度并不是很大,很值得大家学习,一起来学习吧。
2017年最受欢迎的生活服务APP!!生活服务类App的出现为日常生活带来极大便利,我们可以通过手机客户端预约各种家政服务,足不出户就能够享受实惠、便捷的生活服务,那么接下来我为大家推荐一些生活中必备生活app。
由于现在抢红包大热起来,很多app都开始支持发红包抢红包了,因此也出现了微信抢红包神器,可以自动抢微信红包,肯定比认为手动操作快,有消息就会自动打开,然后打开红包。今天就给大家分享2017微信自动抢红包神器大全。
2017手机赚钱软件排行榜!!很多朋友都在苦恼每天的钱不够话,空闲时间不知道怎么打发,总想寻找一种简单的赚钱方式,我们的手机app就有很多,下面具体给大家介绍手机赚钱软件有哪些,总有一款你中意的。
经常玩微信的朋友都知道,微信小程序已经全面上线了,对于小程序的出现是否会引发App变革众说纷纭,但不可否认的是小程序能够带给用户及产商一定的便利,那么今天为大家汇总目前上线的一些实用小程序。
男女交往一直都是人们比较关注的一个问题,都说相爱容易,相处不易,交往过的朋友应该都能深刻的体会到,女生常常抱怨着让男生理解自己,但男生也希望多体谅一下他们。尤其不要老是问一些无法回答的问题来考验他们。
很多朋友都信风水一说,对于自己家里房屋客厅的装修也是有一定要求的,因为客厅的风水关系着整个家居的运势,更关系着主人的财运与健康,因而客厅里的风水禁忌是需要我们谨慎注意的,那么风水学中客厅风水禁忌有哪些呢?
春天已经来临了,夏天也不远了,新的一才刚刚开始,时尚爱美的美眉们最关注的就是春夏服装设计的流行趋势了,早早的把握住时尚趋势走在流行的最尖端,今天就给大家整理分享2017春夏女装设计流行趋势 ,你绝对不能错过的亮点。

我要回帖

更多关于 handler内存泄露 的文章

 

随机推荐