我们在进行C++编码的时候,难免会遇到内存泄漏的时候,特别是一些新手朋友们在编码完成后花大量的时间解决BUG,在该文章我用代码剖析跟踪内存泄漏的原理,熟悉C/C++的朋友都知道内存分配和释放都是要成对调用的,如果某处代码只分配内存,而没有释放就会造成内存泄漏的bug。
下面帖出我写的内存泄漏跟踪类的代码:
MemLeakTrack.h文件
#pragma once
#ifndef INC_TRACK_ALLOC_H
#define INC_TRACK_ALLOC_H
#include
//
// 内存泄漏检测工具
//
// 使用方法:
// 将此对象的头和源文件添加到工程,并在stdafx.h中包含头文件即可
//
void* operator new (size_t size, LPCSTR pszFileName, int nLine);
void operator delete (void* ptr);
#ifdef DEBUG_NEW
#undef DEBUG_NEW
#endif
#define DEBUG_NEW new (__FILE__, __LINE__)
//
class MemLeakTrack
{
class AllocItem
{
public:
AllocItem()
: m_ptr(NULL)
, m_size(0)
, m_pszFileName(NULL)
, m_nLine(0)
{
}
AllocItem(const AllocItem &src)
: m_ptr(src.m_ptr)
, m_size(src.m_size)
, m_pszFileName(src.m_pszFileName)
, m_nLine(src.m_nLine)
{
}
AllocItem(void *ptr, size_t size, LPCSTR pszFileName, int nLine)
: m_ptr(ptr)
, m_size(size)
, m_pszFileName(pszFileName)
, m_nLine(nLine)
{}
AllocItem& operator=(const AllocItem& src)
{
m_ptr = src.m_ptr;
m_size = src.m_size;
m_pszFileName = src.m_pszFileName;
m_nLine = src.m_nLine;
return *this;
}
public:
inline const void* Ptr() const { return m_ptr; }
inline size_t Size() const { return m_size; }
inline LPCSTR FileName() const { return m_pszFileName; }
inline int Line() const { return m_nLine; }
protected:
void* m_ptr; //内存地址
size_t m_size; //分配尺寸
LPCSTR m_pszFileName; //代码文件
int m_nLine; //代码行数
};
class CAllocCounter
{
public:
CAllocCounter()
: m_pszFileName(NULL)
, m_nLine(0)
, m_nCounter(0)
{}
CAllocCounter(LPCSTR pszFileName, int nLine)
: m_pszFileName(pszFileName)
, m_nLine(nLine)
, m_nCounter(0)
{}
CAllocCounter& operator=(const CAllocCounter& src)
{
m_pszFileName = src.m_pszFileName;
m_nLine = src.m_nLine;
m_nCounter = src.m_nCounter;
return *this;
}
inline LPCSTR FileName() const { return m_pszFileName; }
inline int Line() const { return m_nLine; }
inline ULONG Counter() const { return m_nCounter; }
inline void Add() { m_nCounter++; }
protected:
LPCSTR m_pszFileName;
int m_nLine;
ULONG m_nCounter;
};
public:
MemLeakTrack();
~MemLeakTrack();
void Add(void *ptr, size_t size, LPCSTR pszFileName, int nLine);
void Remove(void *ptr);
void Dumps();
static bool IsActive() { return m_bActive; }
protected:
DWORD GetHashCode(LPCSTR pszFileName, int nLine);
protected:
static bool m_bActive; //保证此对象生命周期内可用
std::tr1::unordered_map m_AllocItems;
std::tr1::unordered_map m_AllocCounter;
};
#endif //INC_TRACK_ALLOC_H
MemLeakTrack.cpp文件
#include "stdafx.h"
#include "MemLeakTrack.h"
MemLeakTrack theMemLeakTrack;
void* operator new (size_t size, LPCSTR pszFileName, int nLine)
{
void *ptr = malloc(size);
if (MemLeakTrack::IsActive())
theMemLeakTrack.Add(ptr, size, pszFileName, nLine);
return ptr;
}
void operator delete (void* ptr)
{
if (MemLeakTrack::IsActive())
theMemLeakTrack.Remove(ptr);
free(ptr);
}
//
bool MemLeakTrack::m_bActive = false;
MemLeakTrack::MemLeakTrack()
{
MemLeakTrack::m_bActive = true;
}
MemLeakTrack::~MemLeakTrack()
{
MemLeakTrack::m_bActive = false;
Dumps();
}
void MemLeakTrack::Add(void *ptr, size_t size, LPCSTR pszFileName, int nLine)
{
//使用原生new防止递归调用
#pragma push_macro("new")
#undef new
m_AllocItems[ptr] = AllocItem(ptr, size, pszFileName, nLine);
DWORD dwHash = GetHashCode(pszFileName, nLine);
//分配计数
auto iter = m_AllocCounter.find(dwHash);
if (iter == m_AllocCounter.end())
m_AllocCounter[dwHash] = CAllocCounter(pszFileName, nLine);
m_AllocCounter[dwHash].Add();
#pragma pop_macro("new")
}
void MemLeakTrack::Remove(void *ptr)
{
#pragma push_macro("delete")
#undef delete
auto it = m_AllocItems.find(ptr);
if (it != m_AllocItems.end())
{
m_AllocItems.erase(it);
}
#pragma pop_macro("delete")
}
void MemLeakTrack::Dumps()
{
TCHAR szBuffer[512] = { 0 };
std::vector rankList;
rankList.reserve(m_AllocCounter.size());
for (auto it = m_AllocCounter.begin(); it != m_AllocCounter.end(); ++it)
{
rankList.push_back(it->second);
}
//对排名进行排序
std::sort(rankList.begin(), rankList.end(), [](const CAllocCounter &a, const CAllocCounter &b) {
return a.Counter() > b.Counter();
});
//打印计数排名
OutputDebugString(TEXT("Memory allocation count ranking:\n"));
for (auto &item : rankList)
{
_stprintf_s(szBuffer, _countof(szBuffer), TEXT("Alloc counter:%u \t\tFile(%d) %hs\n"), item.Counter(), item.Line(), item.FileName());
OutputDebugString(szBuffer);
}
//打印未释放的内存
for (auto it = m_AllocItems.begin(); it != m_AllocItems.end(); ++it)
{
_stprintf_s(szBuffer, _countof(szBuffer), TEXT("normal block at 0x%p, %d byte long.\nFile(%d):%hs\n"),
it->first,
it->second.Size(),
it->second.Line(),
it->second.FileName());
OutputDebugString(szBuffer);
}
}
DWORD MemLeakTrack::GetHashCode(LPCSTR pszFileName, int nLine)
{
CHAR szBuffer[512] = { 0 };
sprintf_s(szBuffer, _countof(szBuffer), "File(%d) %s", nLine, pszFileName);
return std::hash{}(szBuffer);
}