C++:重载全局new/delete实现跨平台多线程内存检测
Reference: https://blog.csdn.net/u014023615/article/details/39551191
Reference: https://blog.csdn.net/u014023615/article/details/39551191
实现类:
DumpMemoryLeaks.h
/**
* @file DumpMemoryLeaks.h
* @brief 跟踪内存分配并定时输到文件,以协助检查有无内存泄漏
*
* 修订记录
* @author jack3z
* @version 1.00
* @date 2014-05-18
*
*/
#ifndef DUMPMEMORYLEAKS_H
#define DUMPMEMORYLEAKS_H
#ifdef __linux__
# include <pthread.h>
#else
# include <Windows.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <time.h>
#include <string>
#include <map>
#include <list>
#include <assert.h>
#ifndef DUMP_MEM_REPORT_FREQUENCY
//# define DUMP_MEM_REPORT_FREQUENCY (30*60) //每隔30分钟输出一次内存分配情况
//# define DUMP_MEM_REPORT_FREQUENCY 60 //每隔一分钟输出一次内存分配情况
# define DUMP_MEM_REPORT_FREQUENCY 10 //测试时,10秒输出一次内存分配情况
#endif
struct StMemAllocRec
{
void* addr;
size_t nSize;
};
class CAllocLocalInfo
{
public:
CAllocLocalInfo()
{
m_nLine = -1;
}
std::string m_strFile;
int m_nLine;
bool operator<(const CAllocLocalInfo& other) const
{
return m_strFile < other.m_strFile || (m_strFile==other.m_strFile && m_nLine < other.m_nLine);
}
};
class CDumpMemoryLeaks
{
private:
CDumpMemoryLeaks(void);
~CDumpMemoryLeaks(void);
public:
static CDumpMemoryLeaks& GetInstance()
{
static CDumpMemoryLeaks inst;
return inst;
}
void Init();
void AddTrack(void* addr, size_t asize, const char *fname, int lnum);
void RemoveTrack(void* addr);
protected:
bool IsTheTime2Dump()
{
return m_timeDump < time(NULL);
}
void ResetDumpTime()
{
m_timeDump = time(NULL) + DUMP_MEM_REPORT_FREQUENCY;
}
void Dump();
void lock()
{
#ifdef __linux__
pthread_mutex_lock(&m_mtx);
#else
EnterCriticalSection(&m_mtx);
#endif
}
void unlock()
{
#ifdef __linux__
pthread_mutex_unlock(&m_mtx);
#else
LeaveCriticalSection(&m_mtx);
#endif
}
protected:
bool m_bInit;
FILE* m_fpDumpFile;
std::string m_strDumpFile;
std::map< CAllocLocalInfo, std::list<StMemAllocRec> > m_mapAllocRec;
std::map<void *,CAllocLocalInfo> m_mapAddr2AllocLocal;
time_t m_timeDump;
std::string m_strMsg;
char m_szBuf[1024];
#ifdef __linux__
pthread_mutex_t m_mtx;
#else
CRITICAL_SECTION m_mtx;
#endif
};
//#ifdef DEBUG_REPORT_NEW_ALLOC
inline void * operator new(size_t size, const char* file, const size_t line)
{
void *ptr = (void*)malloc(size);
CDumpMemoryLeaks::GetInstance().AddTrack(ptr, size, file, line);
return(ptr);
}
inline void * operator new [](size_t size, const char* file, const size_t line)
{
void *ptr = (void*)malloc(size);
CDumpMemoryLeaks::GetInstance().AddTrack(ptr, size, file, line);
return ptr;
}
inline void operator delete(void *p)
{
CDumpMemoryLeaks::GetInstance().RemoveTrack(p);
free(p);
}
inline void operator delete(void *p, size_t size)
{
CDumpMemoryLeaks::GetInstance().RemoveTrack(p);
free(p);
}
inline void operator delete(void *p, const char* file, const size_t line)
{
CDumpMemoryLeaks::GetInstance().RemoveTrack(p);
free(p);
}
inline void operator delete [](void *p)
{
CDumpMemoryLeaks::GetInstance().RemoveTrack(p);
free(p);
}
inline void operator delete [](void *p, size_t size)
{
CDumpMemoryLeaks::GetInstance().RemoveTrack(p);
free(p);
}
inline void operator delete [](void *p, const char* file, const size_t line)
{
CDumpMemoryLeaks::GetInstance().RemoveTrack(p);
free(p);
}
#define malloc(s) ((void*)new unsigned char[s])
#define free(p) (delete [] (char*)(p));
#define new new(__FILE__, __LINE__) //1st parameter:size is not needed, passed by the compiler
//#endif
#endif
DumpMemoryLeaks.cpp
#include "DumpMemoryLeaks.h"
#ifndef localtime_r
#if _MSC_VER >= 1400
//Visual C++ 2005 以及更高版本
# define localtime_r(_Time_ptr,_Tm_ptr) (localtime_s((_Tm_ptr),(_Time_ptr)) == 0 ? (_Tm_ptr) : NULL)
#else
# define localtime_r(_Time_ptr,_Tm_ptr) ( *(_Tm_ptr) = *localtime(_Time_ptr), (_Tm_ptr))
#endif //#if _MSC_VER >= 1500
#endif //#ifndef localtime_r
#ifdef _MSC_VER
# ifndef snprintf
# define snprintf _snprintf
# endif//snprintf
#endif //_MSC_VER
CDumpMemoryLeaks::CDumpMemoryLeaks(void)
{
m_bInit = false;
m_fpDumpFile = NULL;
m_timeDump = NULL;
memset(m_szBuf,0,sizeof(m_szBuf));
#ifdef __linux__
pthread_mutexattr_t mattr;
pthread_mutexattr_init(&mattr);
pthread_mutexattr_settype(&mattr , PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&m_mtx,&mattr);
#else
InitializeCriticalSection(&m_mtx);
#endif
}
CDumpMemoryLeaks::~CDumpMemoryLeaks(void)
{
if (m_fpDumpFile)
{
fclose(m_fpDumpFile);
m_fpDumpFile = NULL;
}
#ifdef __linux__
pthread_mutex_destroy(&m_mtx);
#else
DeleteCriticalSection(&m_mtx);
#endif
}
void CDumpMemoryLeaks::Init()
{
lock();
if (!m_bInit)
{
ResetDumpTime();
m_bInit = true;
}
unlock();
}
void CDumpMemoryLeaks::AddTrack(void* addr, size_t asize, const char *fname, int lnum)
{
lock();
if (!m_bInit)
{
Init();
}
CAllocLocalInfo alloc_local;
alloc_local.m_strFile = fname;
alloc_local.m_nLine = lnum;
StMemAllocRec mem_alloc_rec;
mem_alloc_rec.addr = addr;
mem_alloc_rec.nSize = asize;
m_mapAllocRec[alloc_local].push_back(mem_alloc_rec);
m_mapAddr2AllocLocal[addr] = alloc_local;
if (IsTheTime2Dump())
{
Dump();
ResetDumpTime();
}
unlock();
}
void CDumpMemoryLeaks::RemoveTrack(void* addr)
{
lock();
if (!m_bInit)
{
Init();
}
bool bRemoveSuccess = false;
std::map<void *, CAllocLocalInfo>::iterator itorAddr2AllocLocal = m_mapAddr2AllocLocal.find(addr);
if (itorAddr2AllocLocal == m_mapAddr2AllocLocal.end())
{
unlock();
return;
}
else
{
std::map< CAllocLocalInfo, std::list<StMemAllocRec> >::iterator itorAllocRec = m_mapAllocRec.find(itorAddr2AllocLocal->second);
assert(itorAllocRec != m_mapAllocRec.end());
std::list<StMemAllocRec>& listAllocRec = itorAllocRec->second;
for (std::list<StMemAllocRec>::iterator itor = listAllocRec.begin();
itor != listAllocRec.end();
++itor)
{
if ((*itor).addr == addr)
{
listAllocRec.erase(itor);
bRemoveSuccess = true;
break;
}
}
if (listAllocRec.empty())
{
m_mapAllocRec.erase(itorAllocRec);
m_mapAddr2AllocLocal.erase(itorAddr2AllocLocal);
}
}
assert(bRemoveSuccess);
if (IsTheTime2Dump())
{
Dump();
ResetDumpTime();
}
unlock();
}
void CDumpMemoryLeaks::Dump()
{
time_t timeNow = time(NULL);
struct tm tmNow;
if (NULL == localtime_r(&timeNow,&tmNow))
{
assert(false);
}
if (m_strMsg.empty())
{//生成信息
size_t nTotalAlloc = 0;
std::list<CAllocLocalInfo> listLocal;//按内存大到小排序
std::map<CAllocLocalInfo,size_t> mapLocal2Size;
for (std::map< CAllocLocalInfo, std::list<StMemAllocRec> >::iterator itor = m_mapAllocRec.begin();
itor != m_mapAllocRec.end();
++itor)
{
const CAllocLocalInfo& local = itor->first;
std::list<StMemAllocRec>& listAllocRec = itor->second;
for (std::list<StMemAllocRec>::iterator itor = listAllocRec.begin();
itor != listAllocRec.end();
++itor)
{
nTotalAlloc += itor->nSize;
mapLocal2Size[local] += itor->nSize;
}
}
for (std::map<CAllocLocalInfo,size_t>::iterator itor = mapLocal2Size.begin();
itor != mapLocal2Size.end();
++itor)
{
std::list<CAllocLocalInfo>::iterator itorLocalList = listLocal.begin();
for (;
itorLocalList != listLocal.end();
++itorLocalList)
{
if (itor->second >= mapLocal2Size[*itorLocalList])
{
break;
}
}
listLocal.insert(itorLocalList,itor->first);
}
snprintf(m_szBuf,sizeof(m_szBuf)-1,"Total unfree:%lu \n",(unsigned long)nTotalAlloc);
m_strMsg += "\n";
m_strMsg += m_szBuf;
m_strMsg += "--------------------------------------------------------------------------\n";
snprintf(m_szBuf,sizeof(m_szBuf)-1,
"Time: %04u-%02u-%02u %02u:%02u:%02u\n\n",
tmNow.tm_year + 1900,
tmNow.tm_mon + 1,
tmNow.tm_mday,
tmNow.tm_hour,
tmNow.tm_min,
tmNow.tm_sec
);
m_strMsg += m_szBuf;
double dTotalReciprocal = 1.0/(double)nTotalAlloc;
for (std::list<CAllocLocalInfo>::iterator itorLocalList = listLocal.begin();
itorLocalList != listLocal.end();
++itorLocalList)
{
size_t nSize = mapLocal2Size[*itorLocalList];
snprintf(m_szBuf,sizeof(m_szBuf)-1,
"%s:line %d, unfreed size:%lu, percentage:%lf %%; alloc times:%lu\n",
itorLocalList->m_strFile.c_str(),
itorLocalList->m_nLine,
nSize,
nSize*dTotalReciprocal*100,
(unsigned long)m_mapAllocRec[*itorLocalList].size()
);
m_strMsg += m_szBuf;
}
m_strMsg += "--------------------------------------------------------------------------\n";
}
//////////////////////////////////////////////////////////////////////////
//文件操作:打开文件,分割文件并写入
snprintf(m_szBuf,sizeof(m_szBuf)-1,
"MemoryAllocReport_%04u-%02u-%02u.txt",
tmNow.tm_year + 1900,
tmNow.tm_mon + 1,
tmNow.tm_mday
);
if (!m_fpDumpFile)
{
m_fpDumpFile = fopen(m_szBuf,"a");
m_strDumpFile = m_szBuf;
fprintf(m_fpDumpFile, "\n\n---------------- DumpMemoryLeaks begin! ----------------\n");
}
else
{
if (m_strDumpFile != m_szBuf)
{
fclose(m_fpDumpFile);
m_fpDumpFile = fopen(m_szBuf,"w");
m_strDumpFile = m_szBuf;
}
}
fwrite(m_strMsg.c_str(),m_strMsg.length(),1,m_fpDumpFile);
//文件操作结束
//////////////////////////////////////////////////////////////////////////
m_strMsg.clear();
}
实现方法:把文件保存到项目源文件目录下并在全局头文件添加# include "DumpMemoryLeaks.h"
如在win32工程中的stdafx.h文件内添加:
#ifdef _DEBUG
# include "DumpMemoryLeaks.h"
#endif // _DEBUG
测试:
// DbgMemLeak.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <list>
using namespace std;
class A
{
public:
A()
{
m_i = 9;
}
~A()
{
if (m_i != 9)
{
assert(false);
}
m_i = 1;
}
int m_i;
};
class B
{
public:
B()
{
printf("new B instance %p \n", this);
}
~B()
{
printf("delete B instance %p \n", this);
}
};
int main(int argc, _TCHAR* argv[])
{
/*
{//测试构造和析构
B* pB = new B();
delete pB;
Sleep(3*1000);//方便观察终端输出内容
}
*/
{//测试数组构造和析构
B* pArray = new B[4];
delete[] pArray;
pArray = NULL;
Sleep(3*1000);//方便观察终端输出内容
}
/*
{//测试new和delete基础类型数组
char* pChArr = new char[1024];
delete pChArr;
}
*/
//模拟内存泄漏
for (int i = 0; i < 60*30*10; ++i)
{
A* p = new A ;
Sleep(100);
if (i%2 == 0)
{
delete p;
}
}
for (int i = 0; ; ++i)
{
A* p = new A ;
Sleep(1);
if (i%4 == 0)
{
delete p;
}
}
return 0;
}
---------------------
本文来自 jack3z 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/u014023615/article/details/39551191?utm_source=copy
C++:重载全局new/delete实现跨平台多线程内存检测的更多相关文章
- 重载全局new/delete实现内存检测
下面介绍用重载new/delete运算符的方式来实现一个简单的内存泄露检测工具,基本思想是重载全局new/delete运算符,被检测代码调用new和delete运算符时就会调用重载过的operator ...
- 【ThinkingInC++】64、重载new和delete,来模仿内存的分配
/** * 书本:[ThinkingInC++] * 功能:重载new和delete.来模仿内存的分配 * 时间:2014年10月5日14:30:11 * 作者:cutter_point */ #in ...
- 重载operator new delete函数
可以重载global的operator new delete 函数,细节如下: MyNewDelete.h #pragma once #include <stdlib.h> #includ ...
- ZT c++ 中的重载全局new,delete
c++ 中的重载全局new,delete 分类: c++ 2010-08-06 10:31 116人阅读 评论(1) 收藏 举报 deletec++file编译器语言工作 最近做一个小项目,对c++又 ...
- C++ 重载new和delete操作符
原因: C++标准库提供的new和delete操作符,是一个通用实现,未针对具体对象做具体分析 存在分配器速度慢.小型对象空间浪费严重等问题,不适用于对效率和内存有限制的应用场景 好处: 灵活的内 ...
- 重载new和delete来检测内存泄漏
重载new和delete来检测内存泄漏 1. 简述 内存泄漏属于资源泄漏的一种,百度百科将内存泄漏分为四种:常发性内存泄漏.偶发性内存泄漏.一次性内存泄漏和隐式内存泄漏. 常发性指:内存泄漏的代 ...
- 重载new和delete
当我们创建一个new表达式时,会发生两件事.首先使用operator new()分配内存,然后调用构造函数.在delete表达式里,调用了析构函数,然后使用operator delete()释放内存. ...
- C++中基于Crt的内存泄漏检测(重载new和delete,记录在Map里)
尽管这个概念已经让人说滥了 ,还是想简单记录一下, 以备以后查询. #ifdef _DEBUG#define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FIL ...
- c/c++ 重载new,delete运算符 placement new
重载new,delete运算符 new,delete在c++中也被归为运算符,所以可以重载它们. new的行为: 先开辟内存空间 再调用类的构造函数 开辟内存空间的部分,可以被重载. delete的行 ...
随机推荐
- HDU 6114 Chess【逆元+组合数】(组合数模板题)
<题目链接> 题目大意: 車是中国象棋中的一种棋子,它能攻击同一行或同一列中没有其他棋子阻隔的棋子.一天,小度在棋盘上摆起了许多車……他想知道,在一共N×M个点的矩形棋盘中摆最多个数的車使 ...
- hdu 2036 求多边形面积 (凸、凹多边形)
<题目链接> Problem Description “ 改革春风吹满地,不会AC没关系;实在不行回老家,还有一亩三分地.谢谢!(乐队奏乐)” 话说部分学生心态极好,每天就知道游戏,这次考 ...
- Linux系统 vi/vim文本编辑器
Linux系统 vi/vim文本编辑器 (一)Vim/Vi简介 (二)Vim/Vi工作模式 (三)Vim/Vi基本使用 (四)Vim/Vi应用技巧 (一)Vim/Vi简介 Vim/Vi是一个功能强大的 ...
- P1593 因子和
P1593 因子和新算法:#define ni 逆元先质因数分解,(1+p1^1+p1^2...p1^x)*(1+p2^1+p2^2...p2^x)然后套等比数列公式就可以了. #include< ...
- 使用js生成二维码和条形码
1.生成二维码 使用github开源项目qrcode. 1.引入方式一(js cdn引入): ①.引入qrcode cdn: 自行下载..没有合适的cdn,地址 <script src=&quo ...
- BZOJ.1758.[WC2010]重建计划(分数规划 点分治 单调队列/长链剖分 线段树)
题目链接 BZOJ 洛谷 点分治 单调队列: 二分答案,然后判断是否存在一条长度在\([L,R]\)的路径满足权值和非负.可以点分治. 对于(距当前根节点)深度为\(d\)的一条路径,可以用其它子树深 ...
- [HihoCoder1394]网络流四·最小路径覆盖
题目大意:从有向无环图中选出若干点不想交的链,使得这些链覆盖所有的点,并且链的条数最小. 思路:设超级源点$S$.超级汇点$T$.将$N$个点复制一份,分为$A$部和$B$部.对于$A$部的所有点$A ...
- [COGS2554][SYZOJ247][福利]可持久化线段树
思路: 主席树模板. 注意内存的分配,原始的线段树有$2n$个结点,每次更新时最多增加$log(n)$个结点,总共有$q$次询问,所以存储结点的数组大小为$2N+q log(n)$. #include ...
- Codeforces Round #409 (rated, Div. 2, based on VK Cup 2017 Round 2) 题解【ABCDE】
A. Vicious Keyboard 题意:给你一个字符串,里面只会包含VK,这两种字符,然后你可以改变一个字符,你要求VK这个字串出现的次数最多. 题解:数据范围很小,暴力枚举改变哪个字符,然后c ...
- B - 可能的路径(gcd变形)
https://vjudge.net/contest/218366#problem/B 要不是在数学题专题里,我估计就盲目搜索了.10^18范围1s应该过不去. 再细看能感觉到是gcd的变形,但是具体 ...