学习笔记 VS2005内存泄露检查(网络转载)

本文介绍内存泄露的概念,并通过多个示例展示了如何利用_CrtDumpMemoryLeaks()等函数检测内存泄露,包括定位泄露位置及避免误报的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

内存泄露的含义是:拿走了一块“堆”内存块,在某检查点处,发现没有归还这个内存块。
如果是: 地址A = malloc(N); 因为没有调用free(地址A),所以内存泄露了。
如果是: 地址B = new 类型T; 因为没有调用delete 地址B,所以内存泄露了。
如果是:从用户的内存池中取一个内存块,没有调用相应的归还给内存池的操作,也认为是“内存泄露”。
从哪里拿了一个东西,要归还到那个地方去。例如:从图书馆L中借了本书,归还给图书馆B,肯定要挨骂的。
同理,从图书馆L中借了本小说,却还给图书馆一本杂志,也是要挨骂的。

函数_CrtDumpMemoryLeaks()功能:检查内存泄露并且在VC的输出窗口打印出泄露的内存块信息。

例子1 :
#include <stdlib.h>
#include <crtdbg.h>
int main(){
    int* x = new int();
    _CrtDumpMemoryLeaks() 
}

输出:
Detected memory leaks!
Dumping objects ->
{61} normal block at 0x00382650, 4 bytes long.
 Data: <    > 00 00 00 00
非常好,发现了int* x对应的内存块泄露了

例子2 :
template <int Size>
struct Test
{
  Test(){m_p = new char[Size];}
  ~Test(){ delete[] m_p;}
  char* m_p;
};
Test<123> t;
int main(){
    int* x = new int();
    _CrtDumpMemoryLeaks() ;
}

输出:
Detected memory leaks!
{62} normal block at 0x00382708, 4 bytes long.
{61} normal block at 0x00382650, 123 bytes long.
非常不好,它把全局变量t也报告了。这是一个严重的误报。原因是在_CrtDumpMemoryLeaks()调用时,
全局变量t还没有离开生存期呢,所以此时~Test()未调用呢,delete[] m_p还没调用呢。

例子3 :
Test<123> t;
int main(){
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
    int* x = new int();
}

输出:
Detected memory leaks!
{62} normal block at 0x00382708, 4 bytes long.
非常好,通过_CrtSetDbgFlag函数,告知Crt库在程序完全退出时,打印一下内存泄露的情况。
这时,全局变量t已经析构了,所以误报没有了。

例子4 :
#include <string>
using namespace std;

#define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__)
#define new  DEBUG_NEW

int main(){
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
    int* x = new int();
}
输出:
Detected memory leaks!
c:\sdfdfsdf\sdfdfsdf.cpp(14) : {61} normal block at 0x00382650, 4 bytes long.
太酷了!居然在调试的输出窗口中,显示了造成内存泄露的代码位置。双击一下,还能自动跳到文本编辑器
中对应的代码行上。

例子5 :
#include <iostream>
int main(){
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
    std::cout<<"hello world"<<std::endl;
}
输出:
Detected memory leaks!
{62} normal block at 0x00386028, 4480 bytes long.
见鬼了,怎么出现这么一大块内存泄露呢?我用的是STLPort。换成微软自带的STL,就一切正常。
看来是cout这个全局变量中肯定有一大块预分配的IO缓冲区没有释放了。这个误报没解决 :(

例子6 :
int main(){

    _CrtMemState s1, s2, s3;
    _CrtMemCheckpoint( &s1 );
    new int;
    new char;
    _CrtMemCheckpoint( &s2 );
    if ( _CrtMemDifference( &s3, &s1, &s2) ) _CrtMemDumpStatistics( &s3 );

    return 0;
}
输出:
0 bytes in 0 Free Blocks.
5 bytes in 2 Normal Blocks.
0 bytes in 0 CRT Blocks.
0 bytes in 0 Ignore Blocks.
0 bytes in 0 Client Blocks.
Largest number used: 0 bytes.
Total allocations: 5 bytes.
看来,这个API真是不错,不但能寻找从程序开始执行时的内存泄露,还能在任意一段代码段之间找内存泄露

例子7 :
int main(){
    _CrtSetBreakAlloc(62);
    int* x = new int;
    return 0;
}
输出:无。启动调试F5。调试器自动在int* x = new int;语句处暂停。打开:窗口->调用堆栈窗口。
看到一些函数的调用关系,顺着点点看看,居然发现了int* x = new int;这一行。
这个62是怎么知道的呢?原来在内存泄露的输出信息里,如下所示:
Detected memory leaks!
{62} normal block at 0x00382708, 4 bytes long.
大括号中62就是第62次分配内存时,这块内存泄露了。通过_CrtSetBreakAlloc调用,告知Crt库,在
第62次分配内存的调用时,自动暂停程序,让程序员检查函数调用栈。
如何保证下次程序运行时,第62此分配内存的调用就是int* x = new int;这句造成的呢?答案是不能。
如果程序没有复杂的时序相关的逻辑(多线程),输入的值是一定的,则程序每次运行的行为是一定的。

例子8 :
class Init_before_main{
public:
    Init_before_main(){
       _CrtSetBreakAlloc(62);
    }
};
Init_before_main g_tmp;
int main(){
    int* x = new int;
    return 0;
}
这是对例子7的一点改进,保证在int* x = new int;调用之前,调_CrtSetBreakAlloc(62);。
否则,如下的调用顺序可能会漏过了第62次分配调用
int* x = new int;
_CrtSetBreakAlloc(62);

如何自己实现内存泄露检查工具呢?思路很简单,重载new,delete运算符,使用自己宏替换malloc和free。
所有的分配和释放动作必须经过我过手,我才能加入点私货(统计信息等)。这样就可以检查内存泄露了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值