[技术]如何定位运行期的内存泄漏
VeryCD-fengwen发于VeryCD电驴软件开发小组(http://www.VeryCD.com/groups/Emuledev/),转载请注明出处
目录
1. 运行期的内存泄漏 和 _CrtDumpMemoryLeaks
2. 分析过程
3. 如果你的输出信息没有文件和代码行信息怎么办?
4. 附录:代码
------------------------------------------------------------------------------------------------------
1. 运行期的内存泄漏 和 _CrtDumpMemoryLeaks
运行期的内存泄漏,简单来说就是你的程序在运行的过程序中内存占用一直不正常的往上涨。但在程序退出的时候vc又没有内存泄漏的输出。
解决这个问题的关键在于
_CrtDumpMemoryLeaks
这个函数。你可以在网上搜索“_CrtDumpMemoryLeaks”这个关键字会有很多相关的文章。如果想了解更多,或是看这篇文章没看明白。可以看看网上的其他文章。以下说明都以VC2003为例。早些的版本可能有些不一样。如果有异样可参照网上其他文章。
_CrtDumpMemoryLeaks的功能就是会输出目前申请了但还没有释放的内存。以这种形式:
_simpledlgdlg.cpp(111) : {82} normal block at 0x00372E70, 10 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD
这里我们只要关注这一段:
“_simpledlgdlg.cpp(111) : {82}”
前面好理解,_simpledlgdlg.cpp
文件
的第111
行
申请的内存没被释放。
而大括号里的82,表示是从程序最开始算,
第几次申请内存
。就是程序一启动,第一个调用new之类的函数的地方就是{1},第二个就是{2}。而_simpledlgdlg.cpp文件的第111行是第82个申请内存的。
2. 分析过程
好!基本的东西就都具备了。分析过程是这样的。你可以隔一段时间让你的程序调用一下这个函数。(可以在点击某个按钮时,或定时执行)然后比较两次的输出结果。
比如第一次是
file1.cpp(400) : {679}
file1.cpp(300) : {394}
file1.cpp(200) : {182}
file1.cpp(100) : {90}
第二次是
file2.cpp(200) : {3840}
file2.cpp(100) : {2096}
file1.cpp(200) : {182}
可以看到过了一段时间后file1.cpp的3个对内存的申请都释放了,但是file1.cpp(200) : {182}这个没有释放。这时你就可以去看看这段代码的逻辑是真的不要释放呢,还是在哪忘记释放它了。
工具
附录1,2提供的MemDump.h和MemDump.cpp封装一些功能。可以把它们直接加到工程里即可使用。
在你想打日志的地方调用theMemDump.DumpIntoDifferentFile();(注意包含MemDump.h头文件)就会在每次运行时把日志写到工程目录下的memdump-*.txt里。
3. 如果你的输出信息没有文件和代码行信息怎么办?
一般系统的内存申请是不会记录位置的,所以一定会有些输出是没有位置信息的,但如果是你的代码没有输出位置信息那有两种可能:
可能1.
申请内存的那个cpp没有以下代码
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
可以用一个批量的方法把所有的.cpp文件都加上这一句,
不过最好在你的代码副本上做这件事
。注意这段代码要加在所有的#include下面,以及代码段的最上面。而且stdafx.cpp这个文件是不能加这一段的。
附录3的AddMacro2Cpp.php提供了一个php方式的批量处理。把第一行的PurifyDir('D:/t');的参数改成你的工程目录。安装php,执行以下命令行即可。
可能2.
如果完成上面一步,仍没有位置信息,那可以在stdafx.h里加入
4. 附录:代码
1. MemDump.h
#pragma once
class CMemDump
{
public:
CMemDump(void);
~CMemDump(void);
void SetOutputFile(LPCTSTR lpszPathFile); // 设置输出的日志文件,NULL表示输出到默认设备。
void Dump(); // 输出内存泄漏信息
void DumpIntoDifferentFile(); // 自动输出内存泄漏信息到不同的文件。
protected:
CString m_strPathFile;
};
extern CMemDump theMemDump;
2. MemDump.cpp
#include "StdAfx.h"
#include "./memdump.h"
CMemDump theMemDump;
CMemDump::CMemDump(void)
{
}
CMemDump::~CMemDump(void)
{
}
void CMemDump::SetOutputFile(LPCTSTR lpszPathFile)
{
m_strPathFile = lpszPathFile;
}
void CMemDump::Dump()
{
int aiOldModes[3];
_HFILE ahOldFiles[3];
CFile file;
if (!m_strPathFile.IsEmpty())
{
file.Open(m_strPathFile, CFile::modeCreate | CFile::modeReadWrite);
aiOldModes[0] = _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE );
ahOldFiles[0] = _CrtSetReportFile( _CRT_WARN, file.m_hFile );
aiOldModes[1] = _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE );
ahOldFiles[1] = _CrtSetReportFile( _CRT_ERROR, file.m_hFile );
aiOldModes[2] = _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE );
ahOldFiles[2] = _CrtSetReportFile( _CRT_ASSERT, file.m_hFile );
}
_CrtDumpMemoryLeaks();
if (!m_strPathFile.IsEmpty())
{
_CrtSetReportMode( _CRT_WARN, aiOldModes[0] );
_CrtSetReportFile( _CRT_WARN, ahOldFiles[0] );
_CrtSetReportMode( _CRT_ERROR, aiOldModes[1] );
_CrtSetReportFile( _CRT_ERROR, ahOldFiles[1] );
_CrtSetReportMode( _CRT_ASSERT, aiOldModes[2] );
_CrtSetReportFile( _CRT_ASSERT, ahOldFiles[2] );
file.Close();
}
}
void CMemDump::DumpIntoDifferentFile()
{
static int s_iDumpIndex = 0;
s_iDumpIndex++;
CString str;
str.Format(_T("memdump-%02d.txt"), s_iDumpIndex);
theMemDump.SetOutputFile(str);
theMemDump.Dump();
theMemDump.SetOutputFile(NULL);
}
3. AddMacro2Cpp.php
<?php
PurifyDir('D:/t');
function PurifyDir($target_dir)
{
$d = dir($target_dir);
while (false !== ($entry = $d->read())){
$path_file = $target_dir.DIRECTORY_SEPARATOR.$entry;
if (!is_file($path_file)){
if ($entry !== '.' && $entry !== '..'){
PurifyDir($path_file);
}
}
else{
if (preg_match("#^.+/.cpp$#i", $entry)
&& 0 !== strcasecmp($entry, "stdafx.cpp")){
$contents = file_get_contents($path_file);
if (!preg_match("#^/s*/#define/s+new/s+DEBUG_NEW/s*$#m", $contents)){
echo "matched file: ".$path_file."/n";
$contents = AddDefineToFile($contents, "#define new DEBUG_NEW");
file_put_contents($path_file, $contents);
}
}
}
}
}
function AddDefineToFile($content, $define_line)
{
$insert_pos = 0;
if (false !== ($pos = strrpos($content, '#include'))){
if (false !== ($insert_pos = strpos($content, "/n", $pos))){
$insert_pos += 2;
}
}
$sub1 = substr($content, 0, $insert_pos);
$sub2 = $define_line."/r/n";
$sub3 = substr($content, $insert_pos);
return $sub1.$sub2.$sub3;
}