主题: [技术]如何定位运行期的内存泄漏

[技术]如何定位运行期的内存泄漏
   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,执行以下命令行即可。

代码
php AddMacro2Cpp.php



  

可能2.

如果完成上面一步,仍没有位置信息,那可以在stdafx.h里加入

代码
#define _CRTDBG_MAP_ALLOC


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;
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值