在OpenRO项目过程中,遇到了一些Memory Leak的问题,采取了一些预防措施后,发现有利也有弊,在此总结一下。
关于内存泄露,最共性的就是new一个内存块后忘记delete了,对此,我介绍2个措施,shared_ptr 与 DEBUG_NEW。
一,shared_ptr:
这是boost库里的引用计数指针,它的好处是,把资源的释放交给shared_ptr管理,只要内存中存在指针的引用,该指针就不会被释放。通常shared_ptr在程序中是随处复制,拷贝的,之后引用计数增加。每个shared_ptr的作用域完后,引用计数又减少,如果某个引用计数减少为0,则释放该资源。
下面是一个例子:
void otherFunction(shared_ptr param);
std::list< shared_ptr > intList;
for (int i=0; i<100; ++i)
{
intList.push_back(shared_ptr (new int(rand())));
}
otherFunction(intList.pop_front());
显而易见,我们完全不用管int指针的资源释放,shared_ptr可以随处传递:intList与otherFunction等等。
缺点:可以看到,用了shared_ptr后,则程序对它的依赖性过大,任何声明都是shared_ptr xxxx,比如函数参数。还有一个缺点是, 绝对不要两个对象互相引用,比如class A含有shared_ptr ,而class B又含有shared_ptr。这样你就把它搞晕了,不晓得释放谁,最终结果就是2个资源都不会释放。
二,DEBUG_NEW
这是我在金山实习时学到的,VC下可以设置内存泄露检查机制,配合new的重定义,可以很好的预防内存泄露的问题。因为这组代码网上随处可下载,所以也就不涉及啥机密了,使用方法及代码我贴在文章末尾。
不足:貌似没有。。。
三,Direct3D里的资源释放
这个与内存泄露不同,因为D3D里的资源大都是通过Create*()函数创建的,而且涉及COM的引用计数机制,所以shared_ptr与DEBUG_NEW对它不起作用。虽然D3D自带有泄露检查机制,程序退出时有弹窗提示,但是遗憾的是它仅仅是提示,不会告诉你哪里的资源没释放。至于预防,除了小心外,还有一些经验:
一,不仅仅是Create*()系列函数会增加资源的引用计数,某些Get*()系列函数也会增加资源的引用,比如:GetBackBuffer()获得Surface后,使用完毕必须Realease(),这是最容易忽略的地方,其实这些容易出错的地方,一般在MSDN里的"Remarks”栏有专门注明,所以看MSDN一定不要错过"Remarks“栏。
二,DX自带了Debug工具,一定程度上会帮助你调试资源泄露,具体我就不详细说明了,找到这个工具照着界面操作就行了,比如我电脑上的路径是:Utilities/bin/x86/dxcpl.exe
下面贴一个检测内存泄露的Debug Header,使用方法见注释。/* -------------------------------------------------------------------------
// 文件名 : debugnew.h
// 创建者 : 金山训练营
// 创建时间 : 2007-12-29
// 功能描述 : 辅助检查内存泄漏
//
// 修订记录:
// 1. 创建: 2007.12.29.
//
// 说明: 若使用了MFC则不必使用本文件. MFC有自己的检查内存泄露机制.
//
// 使用步骤:
// 1. include此文件.
//
// 2. 在.cpp中增加以下定义:
// #ifdef _DEBUG
// #define new DEBUG_NEW
// #endif
//
// 3. 在程序退出后, 检查VC的output窗口内是否有dump memory leak
//
// $Id: $
// -----------------------------------------------------------------------*/
#ifndef __DEBUGNEW_H__
#define __DEBUGNEW_H__
#ifdef __cplusplus
#ifndef _DEBUG
// release版下不做任何事情
#else
// debug版下重定义new, 以便当出现内存泄漏时自动报警
#include
// #ifndef TCHAR
// #if defined(_UNICODE) || defined(UNICODE)
// typedef wchar_t TCHAR;
// #else
// typedef char TCHAR;
// #endif
// #endif
inline void * __cdecl operator new(unsigned int size,
const char *file, int line)
{
int nFlag = _CrtSetDbgFlag (_CRTDBG_REPORT_FLAG);
nFlag |= _CRTDBG_LEAK_CHECK_DF;
_CrtSetDbgFlag (nFlag);
return ::operator new(size, _NORMAL_BLOCK, file, line);
};
#define DEBUG_NEW new(__FILE__, __LINE__)
//#define new DEBUG_NEW
// 若不同时定义delete, 则在编译时会出现warning
inline void __cdecl operator delete(void* pData,
const char* /* lpszFileName */,
int /* nLine */)
{
::operator delete(pData);
}
#endif // #ifndef _DEBUG
#endif // #ifdef __cplusplus
#endif // #ifndef __DEBUGNEW_H__