c++:一个辅助类让内存泄漏现原形!
前言 对于c++而言,如何查找内存泄漏是程序员亘古不变的话题;解决之道可谓花样繁多。因为最近要用到QT写程序,摆在我面前的第一个重要问题是内存防泄漏。如果能找到一个简单而行之有效的方法,对后续开发大有裨益。久思终得诀窍,本文就详细介绍我对此问题的应对之策。(文末符完整代码)
如何判断内存有泄漏
内存分配和释放对应的操作是new、delete。如何判断内存是否释放干净?其实判断起来非常简单:一个独立的模块整个生存周期内new的个数和delete的个数相等。用伪代码标示如下:
int newCount = 0;
int deleteCount = 0; //new 操作时
new class();
newCount++; //delete 操作时
delete* objPtr;
deleteCount++; //模块结束时
if(newCount != deleteCount)
{
内存有泄漏
}
如果对所有的new和delete操作,加上如上几行代码,就能发现是否有内存泄漏问题。如果采用上面方法解决问题,手段太low了。
我们的方法有如下特点:
1 使用起来超级简单,不增加开发难度。
2 发生内存泄漏时,能定位到具体是哪个类。
托管new delete 操作符
要跟踪所有的new、delete操作,最简单的办法就是托管new、delete。不直接调用系统的操作符,而是用我们自己写的函数处理。在我们的函数内部,则别有洞天; 对new和delete的跟踪和记录就为我所欲也。托管new和delete需用到模板函数,代码如下:
class MemManage
{
//单实例模式
private:
static MemManage* _instance_ptr;
public:
static MemManage* instance()
{
if (_instance_ptr == nullptr)
{
_instance_ptr = new MemManage();
}
return _instance_ptr;
} public:
MemManage(); //new操作 构造函数没有参数
template <typename T>
T* New()
{
ShowOperationMessage<T>(true);
return new T();
}; //new操作 构造函数有1个参数
template <typename T, typename TParam1>
T* New(TParam1 param)
{
ShowOperationMessage<T>(true);
return new T(param);
}; //new操作 构造函数有2个参数
template <typename T, typename TParam1, typename TParam2>
T* New(TParam1 param1, TParam2 param2)
{
ShowOperationMessage<T>(true);
return new T(param1, param2);
}; //delete 操作
template <typename T>
void Delete(T t)
{
if (t == nullptr)
return; ShowOperationMessage<T>(false);
delete t;
}; //记录new delete
template <typename T>
void ShowOperationMessage(bool isNew)
{
//操作符对应的类名称
const type_info& nInfo = typeid(T);
QString className = nInfo.name(); if (isNew)
{
_newCount++;
}
else
{
_deleteCount++;
} if (!_showDetailMessage)
{
return;
} if (isNew)
{
qDebug() << "*New" << className << ":" << _newCount << ":" << _deleteCount;
}
else
{
qDebug() << "Delete" << className << ":" << _newCount << ":" << _deleteCount;
}
}
}
如何使用辅助类
使用起来很简单,示例代码如下:
//*****new和delete使用伪代码 //new操作,需根据构造函数的参数个数调用对应的函数
//构造函数 没有参数
QFile* file = MemManage::instance()->New<QFile>(); //构造函数 有1个参数
QFile* file = MemManage::instance()->New<QFile, QString>("filename"); //构造函数 有2个参数
QFile* file = MemManage::instance()->New<QFile, QString,bool>("filename",true); //delete 只有一种形式
MemManage::instance()->Delete(file);
一个模块调用周期结束 调用下列代码,查看是否有内存泄漏:
void ShowNewDelete(bool isShowDetail)
{
int leftNew = _newCount - _deleteCount;
qDebug() << "***********************";
qDebug() << "total New:" << _newCount << " Delete:" << _deleteCount << " leftNew:" << leftNew;
} MemManage::instance()->ShowNewDelete(true);
//debug输出如下,如果leftNew为0,则没内存泄漏
total New : 166 Delete : 6 leftNew : 160
进一步定位内存泄漏问题
通过判断new和delete的个数是否相等,只是知道了是否有内存泄漏;进一步定位问题,才能方便我们解决问题。如果能定位到操作哪一个类时,发生了内存泄漏,则问题范围就大大缩小。我们可以按类名,记录new和delete操作个数,c++获取类名函数如下:
const type_info &nInfo = typeid(T);
QString className = nInfo.name();
建立一个map表,记录类名对应的操作信息:
//每个类 统计的信息
class MemObjInfo
{
public:
int NewCount = 0;
int DeletCount = 0;
QString ClassName;
}; //map对照表
QMap<QString, MemObjInfo*> _mapMemObjCount; //按类名统计
void AddCount(QString& className, bool isNew)
{
QMap<QString, MemObjInfo*>::ConstIterator i = _mapMemObjCount.find(className);
if (i == _mapMemObjCount.constEnd())
{
MemObjInfo* info = new MemObjInfo();
info->ClassName = className;
if (isNew)
{
info->NewCount++;
}
else
{
info->DeletCount++;
}
_mapMemObjCount.insert(className, info);
}
else
{
MemObjInfo* info = i.value();
if (isNew)
{
info->NewCount++;
}
else
{
info->DeletCount++;
}
}
}
如果有内存泄漏 则会输出如下信息:
如上图,对5个类的操作发送了内存泄漏。比如我们知道了类OfdDocumentPageAttr发生内存泄漏,就很容易定位问题了。
辅助类完整代码:


#ifndef MEMMANAGE_H
#define MEMMANAGE_H #include <QDebug>
#include <QList>
#include <QMutex> class LockRealse
{
public:
LockRealse(QMutex* mutex)
{
_mutex = mutex;
_mutex->lock();
} ~LockRealse()
{
_mutex->unlock();
} private:
QMutex* _mutex;
}; class MemObjInfo
{
public:
int NewCount = 0;
int DeletCount = 0;
QString ClassName;
}; class MemManage
{
private:
static MemManage* _instance_ptr;
public:
static MemManage* instance()
{
if(_instance_ptr==nullptr)
{
_instance_ptr = new MemManage();
}
return _instance_ptr;
} public:
MemManage()
{
_threadMutex = new QMutex();
_newCount = 0;
_deleteCount = 0;
} template <typename T>
T* New()
{
ShowOperationMessage<T>(true);
return new T();
}; template <typename T,typename TParam1>
T* New(TParam1 param)
{
ShowOperationMessage<T>(true);
return new T(param);
}; template <typename T,typename TParam1,typename TParam2>
T* New(TParam1 param1,TParam2 param2)
{
ShowOperationMessage<T>(true);
return new T(param1,param2);
}; template <typename T>
void Delete(T t)
{
if(t == nullptr)
return; ShowOperationMessage<T>(false);
delete t;
}; void ShowNewDelete(bool isShowDetail)
{
int leftNew = _newCount-_deleteCount;
qDebug()<<"***********************";
qDebug()<<"total New:"<<_newCount<<" Delete:"<<_deleteCount<<" leftNew:"<<leftNew; if(isShowDetail)
{
ShowNewDeleteDetail(false);
}
} void SetShowDetail(bool enable)
{
_showDetailMessage = enable;
} template <typename T>
void clearAndDelete(QList<T>& list)
{
foreach(T item ,list)
{
// Delete(item);
} list.clear();
}; private:
template <typename T>
void ShowOperationMessage(bool isNew)
{
LockRealse lock(_threadMutex);
const type_info &nInfo = typeid(T);
QString className = nInfo.name();
className=TrimClassName(className);
AddCount(className,isNew); if(isNew)
{
_newCount++;
}
else
{
_deleteCount++;
} if(!_showDetailMessage)
{
return ;
} if(isNew)
{
qDebug()<<"*New"<<className<<":"<<_newCount<<":"<<_deleteCount;
}
else
{
qDebug()<<"Delete"<<className<<":"<<_newCount<<":"<<_deleteCount;
}
} void AddCount(QString& className,bool isNew)
{
QMap<QString,MemObjInfo*>::ConstIterator i = _mapMemObjCount.find(className);
if(i == _mapMemObjCount.constEnd())
{
MemObjInfo* info = new MemObjInfo();
info->ClassName = className;
if(isNew)
{
info->NewCount++;
}
else
{
info->DeletCount++;
}
_mapMemObjCount.insert(className,info);
}
else
{
MemObjInfo* info = i.value();
if(isNew)
{
info->NewCount++;
}
else
{
info->DeletCount++;
}
}
} void ShowNewDeleteDetail(bool isShowAll)
{
QMap<QString,MemObjInfo*>::ConstIterator i = _mapMemObjCount.cbegin();
for(;i!=_mapMemObjCount.cend();i++)
{
MemObjInfo *info = i.value();
int leftNew =info->NewCount-info->DeletCount ;
if(leftNew!=0)
{
qDebug()<<"*** obj "<<info->ClassName<<" New:"<<info->NewCount
<<" Delete:"<<info->DeletCount
<<" Diff:"<<leftNew;
}
else
{
if(isShowAll)
{
qDebug()<<"obj "<<info->ClassName<<" New:"<<info->NewCount
<<" Delete:"<<info->DeletCount
<<" Diff:"<<leftNew;
}
}
}
} QString TrimClassName(QString& className)
{
int n= className.lastIndexOf(" *");
if(n<0)
return className.trimmed(); return className.mid(0,n).trimmed();
} private:
QMutex *_threadMutex;
int _newCount;
int _deleteCount;
bool _showDetailMessage =false; QMap<QString,MemObjInfo*> _mapMemObjCount;
}; #endif // MEMMANAGE_H
后记 解决内存泄漏的方法很多。本文介绍了一种行之有效的方法。开发一个新项目前,就需确定如何跟踪定位内存泄漏,发现问题越早解决起来越简单。程序开发是循序渐进的过程,一个功能模块开发完成后,需及早确定是否有内存泄漏。防微杜渐,步步为营,方能产出高质量的产品。
c++:一个辅助类让内存泄漏现原形!的更多相关文章
- 一个意想不到的Javascript内存泄漏
原文:http://point.davidglasser.net/2013/06/27/surprising-javascript-memory-leak.html 本周我在Meter的同事追踪到了一 ...
- windows 下面的内存泄漏排查.
内存泄漏排查 一下本人只是简单的介绍一个实用, 如果读者很感兴趣, 可以查阅msdn自己去深入调查相关的API和原理. API 介绍 1. 马上打印泄漏信息:_CrtDumpMemoryLeaks() ...
- .NET 垃圾回收与内存泄漏
> 前言相信大家一定听过,看过甚至遇到过内存泄漏.在 .NET 平台也一定知道有垃圾回收器,它可以让开发人员不必担心内存的释放问题,因为它会自定管理内存.但是在 .NET 平台下进行编程,绝对不 ...
- iOS AFNetworking内存泄漏处理方法
iOS AFN内存泄漏处理方法 细心的你是否也发现AFN的内存泄漏的问题了呢. 在这里给大家提供一个解决AFN内存泄漏的方法. 单例解决AFN内存泄漏 + (AFHTTPSessionManager ...
- java内存泄漏的定位与分析
1.为什么会发生内存泄漏 java 如何检测内在泄漏呢?我们需要一些工具进行检测,并发现内存泄漏问题,不然很容易发生down机问题. 编写java程序最为方便的地方就是我们不需要管理内存的分配和释放, ...
- Android studio 分析内存泄漏
以前用eclipse的时候,我们采用的是DDMS和MAT,不仅使用步骤复杂繁琐,而且要手动排查内存泄漏的位置,操作起来比较麻烦.后来随着Android studio的潮流,我也抛弃了eclipse加入 ...
- python 内存泄漏调试
Python应用程序内存泄漏的调试 Quake Lee quakelee@geekcn.org 新浪网技术(中国)有限公司 Sina Research & Development Python ...
- (翻译)什么是Java的永久代(PermGen)内存泄漏
http://www.codelast.com/?p=7248 转载请注明出处:http://www.codelast.com/ 本文是我对这篇文章的翻译:What is a PermGen leak ...
- .NET垃圾回收与内存泄漏
相信大家一定听过,看过甚至遇到过内存泄漏.在 .NET 平台也一定知道有垃圾回收器,它可以让开发人员不必担心内存的释放问题,因为它会自定管理内存.但是在 .NET 平台下进行编程,绝对不会发生内存泄漏 ...
随机推荐
- codeforces B. Pasha and String
Pasha got a very beautiful string s for his birthday, the string consists of lowercase Latin letters ...
- Codeforces Round #613 (Div. 2) C. Fadi and LCM (数学)
题意:给你一个正整数\(x\),找两个正整数\(a\),\(b\),使得\(lcm(a,b)=x\),并且\(max(a,b)\)最小. 题解:我们知道,\(lcm(a,b)=a*b/gcd(a,b) ...
- XHXJ's LIS HDU - 4352 最长递增序列&数位dp
代码+题解: 1 //题意: 2 //输出在区间[li,ri]中有多少个数是满足这个要求的:这个数的最长递增序列长度等于k 3 //注意是最长序列,可不是子串.子序列是不用紧挨着的 4 // 5 // ...
- CF1462-D. Add to Neighbour and Remove
codeforces1462D 题意: 给出一个由n个数组成的数组,现在你可以对这个数组进行如下操作:将数组中的一个元素加到这个元素的两边中的一边,然后将这个元素删掉.若该元素在最左边,那么该元素不能 ...
- leetcode32 最长游戏括号 dp
有一说一,我觉得这题没有到困难级 要保存之前的状态,感觉是很明显的dp 思路和题解一样 class Solution { public: int longestValidParentheses(str ...
- Gym 101128J Saint John Festival(凸包 + 二分判点和凸包关系)题解
题意:给你一堆黑点一堆红点,问你有最多几个黑点能找到三个红点,使这个黑点在三角形内? 思路:显然红点组成的凸包内的所有黑点都能做到.但是判断黑点和凸包的关系朴素方法使O(n^2),显然超时.那么我现在 ...
- SPC空投火爆来袭!区块链技术落地加速!
经历市场狂热后,区块链逐渐恢复合理性,在政策红利.技术等多力推进下,各行各业开始涌入区块链行业.在这波浪潮中,SPC侧链代币项目显得格外亮眼,其空投已经发放至第二轮,仅SPC空投月收益就达23%左右, ...
- BGV暴涨千倍,未来或将超越YFI领跑DeFi全场!
毫无疑问,YFI在2020年上半年以一己之力掀翻了DeFi市场的热潮.迄今为止,YFI的新鲜资讯从不缺席,最近也是频频登上各大知名媒体热搜.其币价远远超过比特币价格,也让资本市场注意到DeFi市场原来 ...
- sql server 局域网与公网上的发布与订阅
一台局域网的服务器,可以访问公网. 一台云端的服务器. 要求:将局域网中的服务器部分数据库同步到云端的服务器上. 配置情况: win server 2012 是发布服务器. win server 20 ...
- 修改yapf中的列宽限制值
yapf是一款由Google开源的Python代码自动格式化工具,它根据PEP 8规范可以帮我们自动格式化我们的代码,让代码更规范.更漂亮.但是其中最大列宽被限制为80,如果超过80,在格式化时就会被 ...