C++/C中侦测memory leak

环境条件:
1.  支持运算符new、delete的重载.
 
侦测memory leak的特性:
1.  侦测释放memory的方式是否与申请memory方式一致。包括:
new和delete的配对,new []和delete[]的配对。
出现上述的情况,提示为:
Assertion failed: pos < dwUsedSize
并退出。
2.  侦测申请的memory是否都已释放。(需要调用,才能进行判断。)
出现上述情况,提示为:
**!!Memory Leak!!**
char*   : 0x004416e0 ---- ******(内存内容。char[]*代表着申请的数组)
并退出。
 
修改建议:
1.  现在侦测memory leak使用的是固定大小的数组。若需要更大的数组,请自己手动修改代码。若需要变长的数组,请自己申请空间。
注意:申请memory请使用malloc()。否则会导致stack overflow。 注意使用free释放,否则这里就会出现memory leak。
2.  在侦测到申请的memory并未都释放时,如果想将这些memory释放掉,
以保证不出现memory leak。可以:
while (dwUsedSize) {
    if (MemList[dwUsedSize].bMemArray) {
         ::delete[] MemList[dwUsedSize].pMemAddress;
     } else {
        ::delete MemList[dwUsedSize].pMemAddress;
}
}
但是不赞成这样。(应该让使用者养成习惯,自己释放)
 
摘自《C++编程思想》
如果为一个类重载了运算符n e wd e l e t e,那么无论何时创建这个类的一个对象都将调用这些运算符。但如果为这些对象创建一个数组时,将调用全局运算符new( )立即为这个数组分配
足够的内存。全局运算符delete( )被调用来释放这块内存。可以通过为那个类重载数组版本的
运算符n e w [ ]d e l e t e [ ]来控制对象数组的内存分配。
在语句ClassA *pStr = new ClassA();之后调用delete[] pStr;并不会报错。需要这样调用:
::delete[] pStr;这样memory leak就可以侦测出来。
 
 
在使用过程中的问题及解决:
1.  stack中的对象(特指主函数中的对象),在程序退出时对象才被析构。如果在主函数结束之前调用,肯定会引发“memory leak”(假想的“memory leak”)。要解决这一点,考虑到C++里的scope,可以利用一下。
int main(void) { // 程序的入口,因工程不同而不同
   {             // 重要,保证所有的stack对象都在此scope内
   // your code
   }             // scope结束,所有stack对象释放。
   assertLeak(); // 如果leak,信息只会显示在console上
                // 需要对话框的,请调用ASSERTLEAK();
}
2.  因为vc6.0的库也支持memory leak。在自动生成的.cpp文件的开始都会添加:
    #ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
如果定义了_DEBUG宏,就用DEBUG_NEW去替换文件中出现的new操作符。而
#define DEBUG_NEW new
#define DEBUG_NEW new(THIS_FILE, __LINE__)
如果是上面的宏工作,侦测memory leak工作可以实现。
如果是下面的宏工作,就会调用内部的函数:
operator new(size_t nSize, LPCSTR lpszFileName, int nLine),
侦测memory leak工作就不会实现
3.  MFC的类,由于都继承自CObject。而类CObject内部重载了new和delete操作符,无法侦测到MFC类的memory leak。
 
代码实现比较简单,没有添加注释。
使用时将#include "global.h"放在所有#include的前面。 
/************************************* global.h **************************************************/
#ifndef CXX_GLOBAL_H
#define CXX_GLOBAL_H
#include <stdio.h>
#include <stdlib.h>
#include <wtypes.h>
#include <string.h>
#include <assert.h>
/** buffer size */
enum {
    /** small buffer */
    BUFFER_SIZE_4   = ( 4   ),
    BUFFER_SIZE_8   = ( 8   ),
    BUFFER_SIZE_12  = ( 12  ),
    BUFFER_SIZE_16  = ( 16  ),
    BUFFER_SIZE_24  = ( 24  ),
    BUFFER_SIZE_32  = ( 32  ),
    BUFFER_SIZE_64  = ( 64  ),
    BUFFER_SIZE_128 = ( 128 ),
   
    /** big buffer */
    BUFFER_SIZE_256 = ( 256  ),
    BUFFER_SIZE_512 = ( 512  ),
    BUFFER_SIZE_1K  = ( 1024 ),
    BUFFER_SIZE_2K  = ( 2  * BUFFER_SIZE_1K ),
    BUFFER_SIZE_4K  = ( 4  * BUFFER_SIZE_1K ),
    /** large buffer */
    BUFFER_SIZE_8K  = ( 8  * BUFFER_SIZE_1K ),
    BUFFER_SIZE_16K = ( 16 * BUFFER_SIZE_1K ),
    BUFFER_SIZE_32K = ( 32 * BUFFER_SIZE_1K ),
    BUFFER_SIZE_64K = ( 64 * BUFFER_SIZE_1K )
};
#ifdef _DEBUG
/** requested memory trace */
class MemoryTrace {
public:
     virtual ~MemoryTrace() { }
    /** register requested memory */
    static void   NewMemory(const void* pMemBase, BOOL bMemArray,
                     const char* pFileName = NULL, int lineNo = 0);
    /** log-out requested memory */
    static void   DeleteMemory(const void* pMemBase, BOOL bMemArray);
    /** print all leak memory */
    static void   PrintLeak();
    /** assert memory leak */
    static BOOL   IsLeak();

   
protected:
    struct Memory {
        char*     pMemAddress;
        BYTE      bMemArray;
        char      fileName[BUFFER_SIZE_16];
        int       lineNo;
    };
    static Memory     MemList[BUFFER_SIZE_256];    /** stored requested memory pointer */
    static DWORD      dwUsedSize;
protected:
    MemoryTrace() { }
   
protected:
    static void       PrintBuffer(unsigned char* buffer); 
};
/** override new operator */
void*    operator new(size_t size);
void*    operator new[](size_t size);
void*    operator new(size_t size, void* p);
void*    operator new[](size_t size, void* p);
void*    operator new(size_t size, char* p, int lineno);
void*    operator new[](size_t size, char* p, int lineno);
/** override delete operator */
void     operator delete(void* p);
void     operator delete[](void* p);
void     operator delete(void* p, void* q);
void     operator delete[](void* p, void* q);
void     operator delete(void* p, char* q, int lineno);
void     operator delete[](void* p, char* q, int lineno);
#endif  // _DEBUG
/** define memory leak macro */
#ifdef _DEBUG
#define NewLeak        new(strrchr(__FILE__, '//'), __LINE__)  
#define assertLeak()   if (MemoryTrace::IsLeak()) {  /
                             fprintf(stderr, "**!!Memory Leak!!**/n"); /
                             MemoryTrace::PrintLeak(); /
                             abort(); }
#define ASSERTLEAK()   ASSERT(!MemoryTrace::IsLeak())
#else
#define NewLeak        new
#define assertLeak() 
#define ASSERTLEAK()
#endif  // _DEBUG
#endif // CXX_GLOBAL_H
/****************************************************************************************************/
 
/*****************************************global.h***************************************************/
#include "Global.h"
#ifdef _DEBUG
/*===========================================================================*/
/*
MemoryTrace static member initialization
*/
/*===========================================================================*/
MemoryTrace::Memory MemoryTrace::MemList[BUFFER_SIZE_256];
DWORD               MemoryTrace::dwUsedSize = 0;
/*===========================================================================*/
/*
register requested memory
*/
/*===========================================================================*/
void
MemoryTrace::NewMemory(const void* pMemBase,
                       BOOL bMemArray,
                       const char* pFileName,
                       int lineNo)
{
    assert(NULL != pMemBase);
    if (dwUsedSize < sizeof(MemList) / sizeof(Memory)) {
        MemList[dwUsedSize].pMemAddress = (char*)pMemBase;
        MemList[dwUsedSize].bMemArray = (BYTE)bMemArray;
        if (NULL != pFileName) {
            strncpy(MemList[dwUsedSize].fileName, pFileName,
                sizeof(MemList[dwUsedSize].fileName));
            MemList[dwUsedSize].lineNo = lineNo;
        }
        dwUsedSize++;
    }
}
/*===========================================================================*/
/*
log-out requested memory
*/
/*===========================================================================*/
void
MemoryTrace::DeleteMemory(const void* pMemBase, BOOL bMemArray)
{
    assert(NULL != pMemBase);
    DWORD        pos = 0;
    for (pos = 0; pos < dwUsedSize; ++pos) {
        if (MemList[pos].pMemAddress == (char*)pMemBase &&
            MemList[pos].bMemArray == (BYTE)bMemArray) {
            break;
        }
    }
    assert(pos < dwUsedSize);
    dwUsedSize--;
    if (pos < dwUsedSize) {
        MemList[pos].pMemAddress = MemList[dwUsedSize].pMemAddress;
        MemList[pos].bMemArray = MemList[dwUsedSize].bMemArray;       
    }
    MemList[dwUsedSize].pMemAddress = NULL;
    MemList[dwUsedSize].bMemArray = 0;
    MemList[dwUsedSize].fileName[0] = '/0';
    MemList[dwUsedSize].lineNo = 0;
}
/*===========================================================================*/
/*
assert memory leak
*/
/*===========================================================================*/
BOOL
MemoryTrace::IsLeak()
{
    return dwUsedSize > 0;
}
/*===========================================================================*/
/*
print all info about the memory leak
*/
/*===========================================================================*/
void
MemoryTrace::PrintLeak()
{
    const char*     array[] = {"char*   ", "char[]* "};   
   
    for (DWORD pos = 0; pos < dwUsedSize; ++pos) {       
        fprintf(stderr, "%s: 0x%08x ---- ",
                array[MemList[pos].bMemArray],
                MemList[pos].pMemAddress);       
        PrintBuffer((unsigned char*)MemList[pos].pMemAddress);        
    }
}
/*===========================================================================*/
/*
print the content of the memory
*/
/*===========================================================================*/
void
MemoryTrace::PrintBuffer(unsigned char* buffer)
{
    const long      MAX_BUFFER_PRINT = 32L;
    const char*     blank = "          ";
    char            str[16 + 1];
    memset(str, 0x00, sizeof(str));   
    for (long k = 0L; k < MAX_BUFFER_PRINT; ++k) {
        if (0 == k % 16) {
            fprintf(stderr, "/n%s", blank);  // 10 blanks.          
        }
        fprintf(stderr, "%02X ", *(buffer++));
        if (isprint(*buffer)) { // printable character.
            str[k % 16] = (char)*buffer;
        } else {
            str[k % 16] = '.';
        }
        if (0 == (k + 1) % 16) {
            fprintf(stderr, " %s", str);           
        }
    }
    fprintf(stderr, "/n");
}
/*===========================================================================*/
/*
override new operator
*/
/*===========================================================================*/
void*    operator new(size_t size)
{
    void*   pMemAddr = malloc(size);
    MemoryTrace::NewMemory(pMemAddr, 0);
    return  pMemAddr;
}
void*    operator new[](size_t size)
{
    void*   pMemAddr = malloc(size);
    MemoryTrace::NewMemory(pMemAddr, 1);
    return  pMemAddr;
}
void*    operator new(size_t size, void* p)
{
    return p;
}
void*    operator new[](size_t size, void* p)
{
    return p;
}
void*    operator new(size_t size, char* p, int lineno)
{
    void*   pMemAddr = malloc(size);
    MemoryTrace::NewMemory(pMemAddr, 0, p, lineno);
    return  pMemAddr;
}
void*    operator new[](size_t size, char* p, int lineno)
{
    void*   pMemAddr = malloc(size);
    MemoryTrace::NewMemory(pMemAddr, 1, p, lineno);
    return  pMemAddr;
}
/*===========================================================================*/
/*
override delete operator
*/
/*===========================================================================*/
void     operator delete(void* p)
{
    if (NULL != p) {
        MemoryTrace::DeleteMemory(p, 0);
    }
    free(p);
}
void     operator delete(void* p, void* q)
{
    ::operator delete(p);
}
void     operator delete[](void* p)
{
    if (NULL != p) {
        MemoryTrace::DeleteMemory(p, 1);
    }
    free(p);
}
void     operator delete[](void* p, void* q)
{
    ::operator delete[](p);
}
void     operator delete(void* p, char* q, int lineno)
{
    ::operator delete(p);
}
void     operator delete[](void* p, char* q, int lineno)
{
    ::operator delete[](p);
}
#endif  // _DEBUG
/****************************************************************************************************/
对于C语言的侦测memory leak, 因为C中不存在着重载函数这一说法,那就只好使用替换。
void* (*fname)(size_t size) = malloc;
void*
mymalloc(size_t size)
    /** do what you want to do. */
    printf("haha/n");
   
    return malloc(size);
    /** return fname(size); both OK。*/
}
#define malloc mymalloc
大致上就这样了,当然free也可以被替换。完善工作就由你自己的需求而定吧。
总之一点,#define malloc mymalloc这样的语句要放在函数的后面,否则,嘿嘿~~~
测试代码:
class A { };
 
int main()
{
int    *p = new int(3);
int    *q = new int[4];
// q所指向的memory地址:
// CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD FD FD FD FD
printf(“*p = %d/n”, *p);
// delete[] p;   error. Print: Assertion failed: pos < dwUsedSize
delete     p;
// delete   q; error. Print: Assertion failed: pos < dwUsedSize
assertLeak();  
delete[]   q;
 
A    *a = new A();
delete[]   a; // OK. Call ::delete a
assertLeak(); // OK.
}
 
 
祝好运!
 
Jeff
2006-3-15
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值