环境条件:
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 w和d 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
#define CXX_GLOBAL_H
#include <stdio.h>
#include <stdlib.h>
#include <wtypes.h>
#include <string.h>
#include <assert.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 ),
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 )
};
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;
/** 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);
};
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* 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, void* p);
void* operator new(size_t size, char* p, int lineno);
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);
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
#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;
/*===========================================================================*/
/*
* 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);
/*
* 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++;
}
}
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;
}
/*
* 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;
}
/*
* 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 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];
/*
* 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");
}
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;
}
/*
* 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* pMemAddr = malloc(size);
MemoryTrace::NewMemory(pMemAddr, 1);
return pMemAddr;
}
void* operator new(size_t size, void* p)
{
return p;
}
{
return p;
}
void* operator new[](size_t size, void* p)
{
return 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* 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;
}
{
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);
}
/*
* 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);
}
{
::operator delete(p);
}
void operator delete[](void* p)
{
if (NULL != p) {
MemoryTrace::DeleteMemory(p, 1);
}
free(p);
}
{
if (NULL != p) {
MemoryTrace::DeleteMemory(p, 1);
}
free(p);
}
void operator delete[](void* p, void* q)
{
::operator delete[](p);
}
{
::operator delete[](p);
}
void operator delete(void* p, char* q, int lineno)
{
::operator delete(p);
}
{
::operator delete(p);
}
void operator delete[](void* p, char* q, int lineno)
{
::operator delete[](p);
}
{
::operator delete[](p);
}
#endif // _DEBUG
/****************************************************************************************************/
对于C语言的侦测memory leak, 因为C中不存在着重载函数这一说法,那就只好使用替换。
void* (*fname)(size_t size) = malloc;
void*
mymalloc(size_t size)
{
mymalloc(size_t size)
{
/** do what you want to do. */
printf("haha/n");
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