您的位置:首页 > 其它

检测内存泄露的方法:

2008-09-17 10:17 281 查看
http://blog.csdn.net/wei801004/archive/2006/08/11/1050394.aspx
 检测内存泄露的方法:
C++中检测内存泄漏可以引入系统定义的宏来查看,内存在哪个位置泄漏

文件开始处加入下列定义
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

程序退出时加入以下函数:

_CrtDumpMemoryLeaks();

使用环境:VS2003

如果在系统中出现了内存泄露,在输出窗口会出现这样的信息:

Detected memory leaks!
Dumping objects ->
d:/program files/microsoft visual studio .net 2003/vc7/include/crtdbg.h(692) : {47} normal block at 0x003F2C88, 1024 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.

检测内存泄露的常用手段:debugnew收藏

新一篇: 安装 PrintManager 和 SQL Server 所遇到的问题 | 

function StorePage(){d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(keyit=window.open('http://www.365key.com/storeit.aspx?t='+escape(d.title)+'&u='+escape(d.location.href)+'&c='+escape(t),'keyit','scrollbars=no,width=475,height=575,left=75,top=20,status=no,resizable=yes'));keyit.focus();}检测内存泄露的工具:debugnew

网上有一个流传甚广的检测内存泄露的工具:debugnew(debugnew.h/debugnew.cpp)
用法很简单,把debugnew.cpp放在项目里一起编译,需要检测的文件把debugnew.h嵌在文件的最前面。

为方便使用,对源代码做了一些小的改动。

下面是一些简单的说明:
1、new 的重载
void* operator new (size_t size, const char* file, int line);        ⑴
void* operator new [] (size_t size, const char*  file, int line);      ⑵
在需要检测的文件里,重定义new
#define new new(__FILE__, __LINE__)
造成的结果:
ClassName *p = new ClassName; => ClassName *p = new(__FILE__, __LINE__) ClassName;
// 实际会调用void* operator new (size_t size, const char* file, int line);
ClassName **pp = new classname[count]; => ClassName **pp = new(__FILE__, __LINE__) ClassName[count];
// 实际会调用void* operator new [] (size_t size, const char*  file, int line);
这其实是利用了placement new的语法,通过一个简单的宏,就可以把普通的new操作对应到相应的重载( ⑴,⑵ )上去。
2、delete 的重载
void operator delete (void* p, const char* file, int line);                   ⑶
void operator delete [] (void* p, const char* file, int line);                ⑷
void operator delete (void* p);                                               ⑸
void operator delete [] (void* p);                                            ⑹
因为没有类似于placement new的语法,所以就不能用一个宏来替换替换delete了。要调用带有更多信息的delete操作符,只能修改源代码了。
delete p; => delete ( p, __FILE__, __LINE__ );
delete []pp; => delete [] ( pp, __FILE__, __LINE__ );
但这个工作很烦琐,如果并不需要多余的信息的话,简单地重载delete( ⑸,⑹ )就可以了。
3、检测和统计
程序开始时,在debugnew.cpp中会创建一个DebugNewTracer对象
在重载的new操作符( ⑴,⑵ )中,每一次内存分配都会被记录,而在delete( ⑶,⑷,⑸,⑹ )中则会删除相应的记录。
当程序结束,DebugNewTracer对象被销毁,它的析构函数会dump剩余的记录,这就是泄露的内存了。
在原有代码的基础上,增加了记录size的功能,这样可以在每次new和delete时,看到实际占用的内存。所有信息可以dump出来,也可以写入log。

4、在MFC项目里使用debugnew
MFC对检查内存泄露提供了很好的支持,一般情况下,在MFC项目中加入cpp文件时,MFC会自动在文件头加入如下的代码:
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
这时候,就用不上我们自己的debugnew了。

现在,你有一个MFC项目,另外有非MFC的C++项目,MFC项目要引用 C++项目提供的算法。
现在,你有一个MFC项目,另外有非MFC的C++项目,MFC项目要引用 C++项目提供的算法。通常的做法是:将C++项目编译成一个*,lib,然后和MFC项目一起链接。
那么问题出现了:
MFC能检测到MFC项目中的内存泄露并给出具体的文件名及行号,对于C++项目,尽管可以检测到泄露,但不能给更多的信息;如果在C++项目中加入debugnew,链接时则会出现错误。
解决办法:
在C++项目中,不要加入debugnew,只在要检测泄露的cpp文件中引用debugnew.h就可以了。

5、源代码

********************************************************
debugnew.h:
/*
 filename: debugnew.h
 
 This code is based on code retrieved from a web site. The
 author was not identified, so thanks go to anonymous.
 This is used to substitute a version of the new operator that
 can be used for debugging memory leaks. To use it:
 
 - In any (all?) code files #include debugnew.h. Make sure all
  system files (i.e. those in <>'s) are #included before
  debugnew.h, and that any header files for your own code
  are #included after debugnew.h. The reason is that some
  system files refer to ::new, and this will not compile
  if debugnew is in effect. You may still have problems
  if any of your own code refers to ::new, or if any
  of your own files #include system files that use ::new
  and which have not already been #included before
  debugnew.h.
 - Add debugnew.cpp to the CodeWarrior project or compile
  it into your Linux executable. If debugnew.cpp is in the
  project, then debugnew.h must be #included in at least
  one source file
*/
#ifndef __DEBUGNEW_H__
#define __DEBUGNEW_H__
#include <map>
#define LOG_FILE
#if defined(LOG_FILE)
#define LOG_FILE_NAME "./debugnew.log"
#endif
void* operator new (std::size_t size, const char* file, int line);
void operator delete (void* p, const char* name, int line);
void* operator new [] (std::size_t size, const char* file, int line);
void operator delete [] (void* p, const char* name, int line);
class DebugNewTracer  {
private:
 
 class Entry  {
 public:
  Entry (char const* file, int line) : _file (file), _line (line) {}
  Entry (char const* file, int line, int size) : _file (file), _line (line), _size (size) {}
  Entry () : _file (0), _line (0), _size (0) {}
  const char* File () const { return _file; }
  int Line () const { return _line; }
  size_t Size () const { return _size; }
 private:
  char const* _file;
  int _line;
  size_t _size;
 };
 class Lock {
 public:
  Lock (DebugNewTracer & DebugNewTracer) : _DebugNewTracer (DebugNewTracer) {
   _DebugNewTracer.lock ();
  }
  ~Lock () {
   _DebugNewTracer.unlock ();
  }
 private:
  DebugNewTracer & _DebugNewTracer;
 };
 typedef std::map<void*, Entry>::iterator iterator;
 friend class Lock;
 public:
 DebugNewTracer ();
 ~DebugNewTracer ();
 void Add (void* p, const char* file, int line);
 void Add (void* p, const char* file, int line, size_t size);
 void Remove (void* p);
 void Dump ();
 static bool Ready;
 private:
 void lock () { _lockCount++; }
 void unlock () { _lockCount--; }
 private:
 std::map<void*, Entry> _map;
 int _lockCount;
 size_t _totalsize;
#if defined(LOG_FILE)
 FILE* _logfp;
#endif
 };
 // The file that implements class DebugNewTracer
 // does NOT want the word "new" expanded.
 // The object DebugNewTrace is defined in that
 // implementation file but only declared in any other file.
#ifdef DEBUGNEW_CPP
DebugNewTracer DebugNewTrace;
#else
#define new new(__FILE__, __LINE__)
extern DebugNewTracer DebugNewTrace;
#endif
#endif//#ifndef __DEBUGNEW_H__

********************************************************
debugnew.cpp:
/*
 filename: debugnew.cpp
 This is used to substitute a version of the new operator that
 can be used for debugging memory leaks. In any (all?) code
 files #include debugnew.h. Add debugnew.cpp to the project.
*/
#include <iostream>
#include <map>
using namespace std;
 // This disables macro expansion of "new".
 // This statement should only appear in this file.
#define DEBUGNEW_CPP
#include "debugnew.h"
DebugNewTracer::DebugNewTracer () : _lockCount (0)
{
  // Once this object is constructed all calls to
  // new should be traced.
 Ready = true;
 _totalsize = 0;
#if defined(LOG_FILE)
 if( (_logfp=fopen(LOG_FILE_NAME,"wt")) == NULL )
 {
  printf(" Error! file: debugnew.log can not open@!/n");
  return;
 } 
 fprintf(_logfp,"new, delete list:/n");
 fflush(_logfp);
#endif
 
}
        
void DebugNewTracer::Add (void* p, const char* file, int line)
{
  // Tracing must not be done a second time
  // while it is already
  // in the middle of executing
 if (_lockCount > 0)
  return;
   // Tracing must be disabled while the map
   // is in use in case it calls new.
 DebugNewTracer::Lock lock (*this);
 _map [p] = Entry (file, line);
}
void DebugNewTracer::Add (void* p, const char* file, int line, size_t size)
{
 // Tracing must not be done a second time
 // while it is already
 // in the middle of executing
 if (_lockCount > 0)
  return;
  // Tracing must be disabled while the map
  // is in use in case it calls new.
 DebugNewTracer::Lock lock (*this);
#if 1
 //´Óȫ·¾¶ÖÐÌáÈ¡ÎļþÃû
 //linuxϵÄgcc,__FILE__½ö½ö°üÀ¨ÎļþÃû£¬windowsϵÄvc,__FILE__°üÀ¨È«Â·¾¶,ËùÒÔÓÐÕâÑùµÄ´¦Àí
 file = strrchr(file,'//')== NULL?file:strrchr(file,'//')+1;
#endif
 _map [p] = Entry (file, line, size);
 _totalsize += size;
#if defined(LOG_FILE)
 fprintf(_logfp,"*N p = 0x%08x, size = %6d,  %-22s, Line: %4d.  totalsize =%8d/n", p, size, file, line, _totalsize);
 fflush(_logfp);
#endif
}

void DebugNewTracer::Remove (void* p)
{
 // Tracing must not be done a second time
 // while it is already
 // in the middle of executing
 if (_lockCount > 0)
  return;
  // Tracing must be disabled while the map
  // is in use in case it calls new.
 DebugNewTracer::Lock lock (*this);
 iterator it = _map.find (p);
 if (it != _map.end ())
 {
  size_t size = (*it).second.Size();
  _totalsize -= size;
#if defined(LOG_FILE)
  fprintf(_logfp,"#D
p = 0x%08x, size = %6u.%39stotalsize =%8d/n", p, size,
"-----------------------------------  ", _totalsize );
  fflush(_logfp);
#endif
  _map.erase (it);
 }
 else
 {
#if defined(LOG_FILE)
  fprintf(_logfp,"#D p = 0x%08x. error point!!!/n", p );
  fflush(_logfp);
#endif
 }
}
DebugNewTracer::~DebugNewTracer ()
{
 // Trace must not be called if Dump indirectly
 // invokes new, so it must be disabled before
 // Dump is called. After this destructor executes
 // any other global objects that get destructed
 // should not do any tracing.
 Ready = false;
 Dump ();
#if defined(LOG_FILE)
 fclose(_logfp);
#endif
}
// If some global object is destructed after DebugNewTracer
// and if that object calls new, it should not trace that
// call to new.
void DebugNewTracer::Dump ()
{
 if (_map.size () != 0)
 {
  std::cout << _map.size () << " memory leaks detected/n";
#if defined(LOG_FILE)
  fprintf(_logfp, "/n/n***%d memory leaks detected/n", _map.size ());
  fflush(_logfp);
#endif
  for (iterator it = _map.begin (); it != _map.end (); ++it)
  {
   char const * file = it->second.File ();
   int line = it->second.Line ();
   int size = it->second.Size ();
   std::cout << file << ", "  << line << std::endl;
#if defined(LOG_FILE)
   fprintf(_logfp,"%s, %d, size=%d/n", file, line, size);
   fflush(_logfp);
#endif
  }
 }
 else
 {
  std::cout << "no leaks detected/n";
#if defined(LOG_FILE)
  fprintf(_logfp,"no leaks detected/n");
  fflush(_logfp);
#endif
 }
}
// If some global object is constructed before DebugNewTracer
// and if that object calls new, it should not trace that
// call to new.
bool DebugNewTracer::Ready = false;
void* operator new (size_t size, const char* file, int line)
{
 void * p = malloc (size);
 if (DebugNewTracer::Ready)
  DebugNewTrace.Add (p, file, line, size);
 return p;
}
void operator delete (void* p, const char* file, int line)
{
 file = 0; // avoid a warning about argument not used in function
 line = 0; // avoid a warning about argument not used in function
 
 if (DebugNewTracer::Ready)
  DebugNewTrace.Remove (p);
 free (p);
}
        
void* operator new [] (size_t size, const char*  file, int line)
{
 void * p = malloc (size);
 if (DebugNewTracer::Ready)
  DebugNewTrace.Add (p, file, line, size);
 return p;
}
void operator delete [] (void* p, const char* file, int line)
{
 file = 0; // avoid a warning about argument not used in function
 line = 0; // avoid a warning about argument not used in function
 
 if (DebugNewTracer::Ready)
   DebugNewTrace.Remove (p);
 free (p);
}
        
void* operator new (size_t size)
{
 void * p = malloc (size);
 // When uncommented these lines cause entries in the map for calls to new
 // that were not altered to the debugnew version. These are likely calls
 // in library functions and the presence in the dump of these entries
 // is usually misleading.
 // if (DebugNewTracer::Ready)
 //   DebugNewTrace.Add (p, "?", 0);
 return p;
}
 
void operator delete (void* p)
{
 if (DebugNewTracer::Ready)
  DebugNewTrace.Remove (p);
 free (p);
}
//add by yugang
void operator delete [] (void* p)
{
 if (DebugNewTracer::Ready)
  DebugNewTrace.Remove (p);
 free (p);
}

发表于 @ 2004年12月30日 09:54:00|评论(0AddFeedbackCountStack("234178"))|编辑

新一篇: 安装 PrintManager 和 SQL Server 所遇到的问题 | 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: