首页 | 乐园 | 淘宝店 | 社区 | 电驴 | 网页游戏 | 网址大全

VeryCD / 社区 / 电驴软件开发 / 精华

资源管理小组

相关主题

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

相关分类:

fengwen (斑竹) 2008/03/13 17:58:20 顶楼 举报

[技术]如何定位运行期的内存泄漏
   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;
}

分享到开心网  分享到校内  收藏到QQ书签    订阅本主題RSS更新  美味书签

2008/03/13 18:24:39fengwen 最后编辑 | 查看全部

insulted 2009/06/01 12:04:55 2楼 举报

好文章!


[0] [0] [回复]

3楼已被删除

HeroismSun 2009/07/10 16:53:38 4楼 举报

我要努力学习。


[0] [0] [回复]

wangzhong 2009/09/29 16:22:46 5楼 举报

不错。学习了。


[0] [0] [回复]

lqp64 2009/10/22 09:16:11 6楼 举报

挺深奥的。。。


[0] [0] [回复]

zm3d_008 2009/10/28 10:34:14 7楼 举报

bingo!


[+1] [0] [回复]

平平88 2009/11/30 15:11:33 8楼 举报

不懂


[0] [0] [回复]

平平88 2009/11/30 15:12:18 9楼 举报

不懂


[0] [0] [回复]


返回组首页

快速回复

(?) 附件上传

关于我们 | 诚聘英才 | 著作权声明 | 合作信息 | 广告事务
沪ICP备05001009号
©2003 - 2009 VeryCD.com Some Rights Reserved.