C/C++应用程序内存泄漏检查统计方案
一、前绪
C/C++程序给某些程序员的几大印象之一就是内存自己管理容易泄漏容易崩,笔者曾经在一个产品中使用C语言开发维护部分模块,只要产品有内存泄漏和崩溃的问题,就被甩锅“我的程序是C#开发的内存都是托管的,C++那边也没有内存(庇护其好友),肯定是C这边的问题”(话说一个十几年的程序员还停留在语言层面不觉得有点low吗),笔者毕业不到一年,听到此语心里一万头草泥马奔腾而过,默默地修改了程序,注意不是修改bug(哈哈),而是把所有malloc和free都替换成了自定义宏MALLOC和FREE,debug版本所有内存分配释放都打了日志,程序结束自动报告类似“Core Memory Leaks: 字节数”,此后内存泄漏的问题再也没人敢甩过来了。语言仅仅是个工具,人心是大道。
二、C程序内存泄漏检测方案参考
C语言应用程序一般使用malloc和free两个函数分配和释放内存,对它们做内存泄漏检测还是很好想到完美方案的。所谓的完美:1)当内存泄漏时能迅速定位到是哪一行代码分配的;2)使用简单与原先无异;3)release时或者不需要调试内存的时候,仍然使用原生态函数,不影响效率。
#ifdef DEBUG_MEMORY
#define MALLOC(size) MallocDebug(__FILE__, __LINE__, size)
#define FREE(p) FreeDebug(__FILE__, __LINE__, p)
#else
#define MALLOC(size) malloc(size)
#define FREE(p) free(p)
#endif #ifdef DEBUG_MEMORY
#define MEM_OP_MALLOC 1
#define MEM_OP_FREE 0 void LogMemory(const char* file, int line, void* p, int operation, size_t size); void* MallocDebug(const char* file, int line, size_t size)
{
void* p = malloc(size);
LogMemory(file, line, p, MEM_OP_MALLOC, size);
return p;
} void FreeDebug(const char* file, int line, void* p)
{
LogMemory(file, line, p, MEM_OP_FREE, );
free(p);
} void LogMemory(const char* file, int line, void* p, int operation, size_t size)
{
//打印日志(malloc/free、指针、文件名、行号、指针、第几次分配的序号),分配序号可以实现类似与crtdbg的CrtSetBreakAlloc函数的功能
//操作为malloc时,向map插入一条记录,增加内存使用大小;
//操作为free时,在map中找到记录并删除,减少内存使用大小。
} void DetectMemoryLeaks()
{
//打印当前内存管理的map中剩余的没有释放的内存指针、文件名、行号、大小、分配序号
} #endif void Program()
{
int *pArray = MALLOC(sizeof(int) * );
FREE(pArray); #ifdef DEBUG_MEMORY
DetectMemoryLeaks();
#endif
}
C语言应用程序中的上述内存泄漏检测方案至此完美收官,记录分配序号,也可以向CrtSetBreakAlloc那样调试内存泄漏哦。
三、C++程序内存泄漏检测方案参考
近期在跟踪C++项目的内存泄漏,项目包含多个工程(1个exe+多个自开发dll+多个第三方dll)。
1.首先考虑的第一个方案是利用crtdbg。踩得第一个坑是记得看下工程配置运行时库选项用debug版本(/MTd或/MDd),否则无效。非MFC程序报不出可疑泄漏内存的文件名及行号,要在整个程序所有使用new的文件中包含"#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)"的宏定义。对于单个工程程序而言调试比较简单方便;对于多个dll尤其是有第三方库时,/MTd配置下要非常小心,/MDd配置要好很多,但实际中使用crtdbg调试还是偶尔会崩在系统底层内存分配的地方,出现的问题不在个人解决能力之内,放弃了。
2.其次的第二个方案,考虑自己重载operator new和operator delete,当然是要重载operator new(size_t size, const char* file, int line)这个版本才能在泄漏时定位到行号。同样也是要所有使用new的文件中包含"#define new new( __FILE__, __LINE__)"的宏定义。问题是虽然可以重载operator delete(void* p, const char* file, int line)这个版本,但是这个版本只会在placement new失败时才会调用,正常时候还是调用的operator delete(void* p)版本,所以还需要重载operator delete(void* p)版本,问题是没有重载的系统内置的operator new(size_t size)版本分配的所有内存也会走用户重载后的operator delete(void* p)版本,不配对,一起把operator new(size_t size)也重载了。
第二个方案的另外一个问题是程序要包含宏"#define new new( __FILE__, __LINE__)",但第三方库头文件中有placement new的用法new(pointer)classA(),项目大一点头文件顺序不好调,编译失败。还有就是这个方案实践中(多dll全部设置的相同的运行时库配置)也在系统底层分配内存的方法崩溃过,也可能是个人在哪里的处理有问题,总之不再考虑前两个方案了,打算在应用层做处理。
3.最后确定在最上层想方案,首先C++不能自定义操作符,否则就能定义一个操作符A* pA = debugnew A(1, 2)了。宏不能有空格只能考虑函数debugnew(A, 1, 2)了。下面上方案。
所有要分配或释放内存的文件中包含DebugMemory.h头文件(伪代码):
//文件名:DebugMemory.h #ifdef DEBUG_MEMORY
#define NEW(T, ...) DebugNew<T>(__FILE__, __LINE__, __VA_ARGS__)
#define DEL(p) DebugDelete(__FILE__, __LINE__, p)
#define NEW_ARRAY(T, size) DebugNewArray<T>(__FILE__, __LINE__, size)
#define DEL_ARRAY(p) DebugDeleteArray(__FILE__, __LINE__, p)
#else
#define NEW(T, ...) new T(__VA_ARGS__)
#define DEL(p) delete(p)
#define NEW_ARRAY(T, size) new T[size]
#define DEL_ARRAY(p) delete[] p
#endif #ifdef DEBUG_MEMORY template<class T, class... Args>
T* DebugNew(const char* file, int line, Args&&... args)
{
T* p = new T(std::forward<Args>(args)...);
//todo:记录操作(new)、指针、文件、行号、分配号
return p;
} template<class T>
void DebugDelete(const char* file, int line, T* p)
{
//todo:记录操作(delete)、指针、文件、行号
delete p;
} template<class T>
T* DebugNewArray(const char* file, int line, size_t size)
{
T* p = new T[size];
//todo:记录操作(new[])、指针、文件、行号、分配号
return p;
} template<class T>
void DebugDeleteArray(const char* file, int line, T* p)
{
//todo:记录操作(delete)、指针、文件、行号
delete[] p;
} void DetectMemoryLeaks()
{
//todo:统计并打印未释放的内存信息
} #endif
使用DebugMemory.h头文件:
//文件名:main.cpp #include "DebugMemory.h" class A
{
public:
A(){}
A(int a, int b):m_a(a), m_b(b){}
private:
int m_a;
int m_b;
} int main()
{
A* pA = NEW(A, , ); //new A(1, 2)
DEL(pA); //delete pA; A* pArray = NEW_ARRAY(A, ); //new A[10]
DEL_ARRAY(pArray); //delete[] pArray #ifdef DEBUG_MEMORY
DetectMemoryLeaks(); //内存泄漏检测
#endif return ;
}
四、方案评价
1.C语言应用程序的内存泄漏解决方案:完美。
2.C++语言应用程序的内存泄漏解决方案
优点:没有改变默认的operator new和operator delete行为,毕竟危险。
优点:实用性通用性强,完全在应用程序员的控制范围内。因为在应用层,不管什么版本都可以检测内存泄漏,不用考虑跨dll调用产生的问题。
不足:写法习惯改变,原来是new A(1,2),要写成NEW(A, 1, 2),如果C++能实现自定义操作符,那么方案就完美了。
C/C++应用程序内存泄漏检查统计方案的更多相关文章
- Windows平台上C++开发内存泄漏检查方法
充分的利用调试工具可以非常方便地避免内存泄漏问题. 这里介绍两种方法,互为补充,第一种是VC编译器提供的方法,第二种是专用的内存泄漏检查工具Memmory Validator.这两种方法的基本原理是一 ...
- valgrind--CPP程序内存泄露检查工具
内存泄漏是c++程序常见的问题了,特别是服务类程序,当系统模块过多或者逻辑复杂后,很难通过代码看出内存泄漏. valgrind是一个开源的,检测c++程序内存泄漏有效工具,编译时加上-g选项可以定位到 ...
- Java虚拟机性能管理神器 - VisualVM(6) 排查JAVA应用程序内存泄漏【转】
Java虚拟机性能管理神器 - VisualVM(6) 排查JAVA应用程序内存泄漏[转] 标签: javajvm内存泄漏监控工具 2015-03-11 18:30 1870人阅读 评论(0) 收藏 ...
- c# 内存泄漏检查心得
系统环境 windows 7 x64 检查工具:ANTS Memory Profiler 7 或者 .NET Memory Profiler 4.0 开发的软件为winform / windows s ...
- C++程序内存泄漏检测方法
一.前言 在Linux平台上有valgrind可以非常方便的帮助我们定位内存泄漏,因为Linux在开发领域的使用场景大多是跑服务器,再加上它的开源属性,相对而言,处理问题容易形成“统一”的标准.而在W ...
- windbg分析net程序内存泄漏问题
1 问题简介 有客户反馈,打了最新补丁后,服务器的内存暴涨,一直降不下来,程序非常卡.在客户的服务器上抓了一个dump文件,开始分析. 分析问题的思路: 1.找到是那些资源占用了大量内存? ...
- BCB:内存泄漏检查工具CodeGuard
一.为什么写这篇东西 自己在使用BCB5写一些程序时需要检查很多东西,例如内存泄漏.资源是否有释放等等,在使用了很多工具后,发觉BCB5本身自带的工具―CodeGuard,非常不错,使用也挺方便的,但 ...
- 【转】Unix下C程序内存泄漏检测工具Valgrind安装与使用
Valgrind是一款用于内存调试.内存泄漏检测以及性能分析的软件开发工具. Valgrind的最初作者是Julian Seward,他于2006年由于在开发Valgrind上的工作获得了第二届Goo ...
- .Net程序内存泄漏解析
一.概要 大概在今年三月份的时候突然被紧急调到另外一个项目组解决线上内存泄漏问题.经过两周的玩命奋战终于解决了这个问题这里把心路历程及思路分享给大家.希望可以帮助到各位或现在正遇到这样事情的小伙伴提供 ...
随机推荐
- [数字dp] hdu 3271 SNIBB
意甲冠军:有两个查询: q=1.在[x,y]间隔,兑换b十进制,数字和m多少个月. q=2.在[x,y]间隔,兑换b十进制,数字是m第一k的数目是多少(十进制),没有输出由给定的主题. 思维: 和比特 ...
- IOS开发之iOS深浅拷贝
这里主要侧重于集合类的深浅拷贝,主要事因为工作的时候遇到这个问题. 有不足的地方欢迎指正 首先我们需要有这样的一个前提: [array addObject:obj]; 这样obj的引用计数会增加1,如 ...
- js如何通过变量调用函数,函数名在变量里面
js如何通过变量调用函数,函数名在变量里面. 有时候函数名是动态定义的,这时候我们就需要用到这个方法了. //赋值函数名称 var a = "b"; //定义函数 function ...
- 手把手教你学会 基于JWT的单点登录
最近我们组要给负责的一个管理系统 A 集成另外一个系统 B,为了让用户使用更加便捷,避免多个系统重复登录,希望能够达到这样的效果--用户只需登录一次就能够在这两个系统中进行操作.很明显这就是单点登 ...
- Sequence Models and Long-Short Term Memory Networks
LSTM’s in Pytorch Example: An LSTM for Part-of-Speech Tagging Exercise: Augmenting the LSTM part-of- ...
- asp.net 调用带证书的webservice解决办法
最近在朋友弄一个调整省政府政务工作流的程序.. 需要把当前的信息推送到政务网上,采用的是带证书的https webservice.. 下面说一下实现过程 第一步,引用webservice地址,删除we ...
- mysql练习(一)
练习一 创建表,并插入相关数据 CREATE TABLE email ( ID INT NOT NULL PRIMARY KEY, Email VARCHAR() ) INSERT INTO emai ...
- UWP中的消息提示框(二)
在UWP中的消息提示框(一)中介绍了一些常见的需要用户主动去干涉的一些消息提示框,接下来打算聊聊不需要用户主动去干涉的一些消息提示框.效果就是像双击退出的那种提示框. 先说说比较简单的吧,通过系统To ...
- xgboost参数及调参
常规参数General Parameters booster[default=gbtree]:选择基分类器,可以是:gbtree,gblinear或者dart.gbtree和draf基于树模型,而gb ...
- Qt5.5.0在Linux下静态编译(加上-fontconfig编译项才能显示中文) good
测试系统环境:Ubuntu12.04 (32bit/64bit)编译软件环境:QT5.5.0 本文章主要介绍Linux下QT静态编译环境的搭建,以及如何编译我们的程序board_driver. 1 ...