如何监测内存泄漏

来源:互联网 发布:arm linux gcc 4.9 编辑:程序博客网 时间:2024/04/29 10:15
转自:http://zhidao.baidu.com/question/176936319.html
首先,我们检查了代码,发现所有的代码都是用new来分配内存,用delete来释放内存。那么,我们能够用一个全程替换,来替换掉所有的new和delete操作符吗?不能。因为代码的规模太大了,那样做除了浪费时间没有别的任何好处。好在我们的源代码是用C++来写成的,所以,这意味着没有必要替换掉所有的new和delete,而只用重载这两个操作符。对了,值用重载这两个操作符,我们就能在分配和释放内存之前做点什么。这是一个绝对的好消息。我们也知道该如何去做。因为,MFC也是这么做的。我们需要做的是:跟踪所有的内存分配和交互引用以及内存释放。我们的源代码使用Visual C++写成,当然这种解决方法也可以很轻松的使用在别的C++代码里面。要做的第一件事情是重载new和delete操作符,它们将会在所有的代码中被使用到。我们在stdafx.h中,加入:      #ifdef _DEBUG      inline void * __cdecl operator new(unsigned int size,                                          const char *file, int line)      {      };      inline void __cdecl operator delete(void *p)      {      };      #endif  这样,我们就重载了new和delete操作符。我们用$ifdef和#endif来包住这两个重载操作符,这样,这两个操作符就不会在发布版本中出现。看一看这段代码,会发现,new操作符有三个参数,它们是,分配的内存大小,出现的文件名,和行号。这对于寻找内存泄漏是必需的和重要的。否则,就会需要很多时间去寻找它们出现的确切地方。加入了这段代码,我们的调用new()的代码仍然是指向只接受一个参数的new操作符,而不是这个接受三个参数的操作符。另外,我们也不想记录所有的new操作符的语句去包含__FILE__和__LINE__参数。我们需要做的是自动的让所有的接受一个参数的new操作符调用接受三个参数的new操作符。这一点可以用一点点小的技巧去做,例如下面的这一段宏定义,      #ifdef _DEBUG      #define DEBUG_NEW new(__FILE__, __LINE__)      #else      #define DEBUG_NEW new      #endif      #define new DEBUG_NEW  现在我们所有的接受一个参数的new操作符都成为了接受三个参数的new操作符号,__FILE__和__LINE__被预编译器自动的插入到其中了。然后,就是作实际的跟踪了。我们需要加入一些例程到我们的重载的函数中去,让它们能够完成分配内存和释放内存的工作。这样来做, #ifdef _DEBUG      inline void * __cdecl operator new(unsigned int size,                                         const char *file, int line)      {void *ptr = (void *)malloc(size);AddTrack((DWORD)ptr, size, file, line);return(ptr);      };      inline void __cdecl operator delete(void *p)      {RemoveTrack((DWORD)p);free(p);      };      #endif另外,还需要用相同的方法来重载new[]和delete[]操作符。这里就省略掉它们了。最后,我们需要提供一套函数AddTrack()和RemoveTrack()。我用STL来维护存储内存分配记录的连接表。这两个函数如下:      typedef struct {DWORD address;DWORD size;char file[64];DWORD line;      } ALLOC_INFO;      typedef list<ALLOC_INFO*> AllocList;      AllocList *allocList;      void AddTrack(DWORD addr, DWORD asize, const char *fname, DWORD lnum)      {ALLOC_INFO *info;if(!allocList) {allocList = new(AllocList);}info = new(ALLOC_INFO);info->address = addr;strncpy(info->file, fname, 63);info->line = lnum;info->size = asize;allocList->insert(allocList->begin(), info);      };      void RemoveTrack(DWORD addr)      {AllocList::iterator i;if(!allocList)return;for(i = allocList->begin(); i != allocList->end(); i++){if((*i)->address == addr){allocList->remove((*i));break;}}      };  现在,在我们的程序退出之前,allocList存储了没有被释放的内存分配。为了看到它们是什么和在哪里被分配的,我们需要打印出allocList中的数据。我使用了Visual C++中的Output窗口来做这件事情。      void DumpUnfreed()      {AllocList::iterator i;DWORD totalSize = 0;char buf[1024];if(!allocList)return;for(i = allocList->begin(); i != allocList->end(); i++) {sprintf(buf, "%-50s: LINE %d, ADDRESS %d %d unfreed ",(*i)->file, (*i)->line, (*i)->address, (*i)->size);OutputDebugString(buf);totalSize += (*i)->size;}sprintf(buf, "----------------------------------------------------------- ");OutputDebugString(buf);sprintf(buf, "Total Unfreed: %d bytes ", totalSize);OutputDebugString(buf);      };  现在我们就有了一个可以重用的代码,用来监测跟踪所有的内存泄漏了。这段代码可以用来加入到所有的项目中去。虽然它不会让你的程序看起来更好,但是起码它能够帮助你检查错误,让程序更加的稳定。
原创粉丝点击