内存泄露的排查
黄国强 2010-5-24
1 内存泄露的现象和危害
在C++中,当我们用 new 分配了一段内存,而在用完了以后忘了用 delete 释放,这样内存泄露就产生了。
内存泄露对于长期运行的程序 是致命的,它会导致系统可用内存越来越少,最终导致将程序崩溃。下面用具体的例子来说明内存泄露的产生,以及解决的方法。
2 例子工程的建立
使用VC7.1(也就是VS2003),创建过程的设置如下:
step1:新建项目,项目选 项是“Visual C++项目/win32/win32控制台项目”
step2:在随后弹出的“应用程序设置”的“应用程序类型”中“选择控制 台应用程序”,并在“添加支持”中选择“MFC”。
3 内存泄露的代码示例1
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
// 初始化 MFC 并在失败时显示错误
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
// TODO: 更改错误代码以符合您的需要
_tprintf(_T("致命错误: MFC 初始化失败/n"));
nRetCode = 1;
}
else
{
// TODO: 在此处为应用程序的行为编写代码。
}
int * p = new int; // 分配了大小为一个int的内存
int * p2 = new int[8]; // 分配了一个int数组的内存
return nRetCode;
}
选择Debug,运行这个程序。
在VC输出栏就会出现下面的提示,说明有内 存泄露存在。(当然这个例子是否有害,在此不讨论)
Detected memory leaks!
Dumping objects ->
d:/temp/meleak/meleak/meleak.cpp(34) : {120} normal block at 0x00346300, 32 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
d:/temp/meleak/meleak/meleak.cpp(33) : {119} normal block at 0x003462C0, 4 bytes long.
Data: < > CD CD CD CD
我们在VC输出栏中双击
“d:/temp/meleak/meleak/meleak.cpp(34) : {120} normal block at 0x00346300, 32 bytes long.”,VC就会自动定位到源码发生内存泄露的位置。(我曾经遇到过好几次,有人居然不知道这个功能。所以特别写下来,希望不是废话)
解决的方法就是分别写上如下两行代码即可。
delete p; // 释放一般指针
delete[] p2; // 释放数组
但是以上方法不好,下面推荐的是另外一种方法。代码如下:
std::auto_ptr<int> p3(new int);
std::vector<int> p4(8);
p = p3.get(); // 这时候的p无需释放
p2 = &p4[0]; // 这时候的p2也无需释放
4 内存泄露的代码示例2
下 面是一个比较隐蔽的例子
class CBase
{
public:
CBase(void){}
~CBase(void){}
};
class CDerive : public CBase
{
public:
CDerive(const CString& strVal):m_strVal(strVal){}
~CDerive(void){}
private:
CString m_strVal;
};
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
// 初始化 MFC 并在失败时显示错误
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
// TODO: 更改错误代码以符合您的需要
_tprintf(_T("致命错误: MFC 初始化失败/n"));
nRetCode = 1;
}
else
{
// TODO: 在此处为应用程序的行为编写代码。
}
std::auto_ptr<CBase> pBase(new CDerive("文本"));
return nRetCode;
}
选择Debug,运行这个程序。
在VC输出栏就会出现下面的提 示,说明有内存泄露存在。
Detected memory leaks!
Dumping objects ->
{124} normal block at 0x00346390, 21 bytes long.
Data: < 2| > D8 BC 32 7C 04 00 00 00 04 00 00 00 01 00 00 00
Object dump complete.
上面的这段提示,并没有给出是哪行代码出了问题,这时我们可以用到下面的方法。
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
_CrtSetBreakAlloc(124); // 加入此行,124就是VC输出栏的{}中的数字
...
}
选择 Debug 运行这个程序。一旦分配到124这个内存,VC就会停下来,这样,我们就可以从调用堆栈中查出是哪一行导致内存泄露。
这个bug的原因是在 ~CBase(void){} 少了个 virtual。下面的是正确的写法。
class CBase
{
public:
CBase(void){}
virtual ~CBase(void){}
};