一、前绪

  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++应用程序内存泄漏检查统计方案的更多相关文章

  1. Windows平台上C++开发内存泄漏检查方法

    充分的利用调试工具可以非常方便地避免内存泄漏问题. 这里介绍两种方法,互为补充,第一种是VC编译器提供的方法,第二种是专用的内存泄漏检查工具Memmory Validator.这两种方法的基本原理是一 ...

  2. valgrind--CPP程序内存泄露检查工具

    内存泄漏是c++程序常见的问题了,特别是服务类程序,当系统模块过多或者逻辑复杂后,很难通过代码看出内存泄漏. valgrind是一个开源的,检测c++程序内存泄漏有效工具,编译时加上-g选项可以定位到 ...

  3. Java虚拟机性能管理神器 - VisualVM(6) 排查JAVA应用程序内存泄漏【转】

    Java虚拟机性能管理神器 - VisualVM(6) 排查JAVA应用程序内存泄漏[转] 标签: javajvm内存泄漏监控工具 2015-03-11 18:30 1870人阅读 评论(0) 收藏  ...

  4. c# 内存泄漏检查心得

    系统环境 windows 7 x64 检查工具:ANTS Memory Profiler 7 或者 .NET Memory Profiler 4.0 开发的软件为winform / windows s ...

  5. C++程序内存泄漏检测方法

    一.前言 在Linux平台上有valgrind可以非常方便的帮助我们定位内存泄漏,因为Linux在开发领域的使用场景大多是跑服务器,再加上它的开源属性,相对而言,处理问题容易形成“统一”的标准.而在W ...

  6. windbg分析net程序内存泄漏问题

    1       问题简介 有客户反馈,打了最新补丁后,服务器的内存暴涨,一直降不下来,程序非常卡.在客户的服务器上抓了一个dump文件,开始分析. 分析问题的思路: 1.找到是那些资源占用了大量内存? ...

  7. BCB:内存泄漏检查工具CodeGuard

    一.为什么写这篇东西 自己在使用BCB5写一些程序时需要检查很多东西,例如内存泄漏.资源是否有释放等等,在使用了很多工具后,发觉BCB5本身自带的工具―CodeGuard,非常不错,使用也挺方便的,但 ...

  8. 【转】Unix下C程序内存泄漏检测工具Valgrind安装与使用

    Valgrind是一款用于内存调试.内存泄漏检测以及性能分析的软件开发工具. Valgrind的最初作者是Julian Seward,他于2006年由于在开发Valgrind上的工作获得了第二届Goo ...

  9. .Net程序内存泄漏解析

    一.概要 大概在今年三月份的时候突然被紧急调到另外一个项目组解决线上内存泄漏问题.经过两周的玩命奋战终于解决了这个问题这里把心路历程及思路分享给大家.希望可以帮助到各位或现在正遇到这样事情的小伙伴提供 ...

随机推荐

  1. WPF_界面_图片/界面/文字模糊解决之道整理

    原文:WPF_界面_图片/界面/文字模糊解决之道整理 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/u010265681/article/detai ...

  2. Ubuntu+NDK编译openssl(为了Android上使用libcurl且支持HTTPS协议)

    为了Android上使用libcurl且支持HTTPS协议,需要依赖openssl,因此先来了解一下如何编译OpenSSL1.编译ARM下的共享库(默认的)我使用的是guardianproject的o ...

  3. [转]完美解决)Tomcat启动提示At least one JAR was scanned for TLDs yet contained no TLDs

    一.文章前言    本文是亲测有效解决At least one JAR was scanned for TLDs yet contained no TLDs问题,绝对不是为了积分随便粘贴复制然后压根都 ...

  4. Android开发四大件

    四大组件 Activity Activity是Android应用程序的界面,比如查看联系人.打电话.玩游戏的界面等一个应用程序通常包含多个Activity,即多个界面Activity通过布局管理各种V ...

  5. 最简单的IdentityServer实现——Api

    1.创建项目并添加引用 创建ASP.NET Core Web API项目IdentityServer.EasyDemo.Api   1   2 引用IdentityServer4.AccessToke ...

  6. 运行control userpasswords2实现winXP自动登录

    原文:运行control userpasswords2实现winXP自动登录 如果你的计算机只是自己一人在用,且每次都用同一个用户名(或者你根本没在意过什么是用户名),而每次都要输入密码,是否太麻烦了 ...

  7. delphi判断线程状态函数(使用GetExitCodeThread API函数去判断线程的句柄)

    //判断线程是否释放//返回值:0-已释放:1-正在运行:2-已终止但未释放://3-未建立或不存在 function CheckThreadFreed(aThread: TThread): Byte ...

  8. C# Newtonsoft.Json JObject移除属性,在序列化时忽略

    原文 C# Newtonsoft.Json JObject移除属性,在序列化时忽略 一.针对 单个 对象移除属性,序列化时忽略处理 JObject实例的 Remove() 方法,可以在 指定序列化时移 ...

  9. delphi dom动态创建节点方法

    在一次测试demo中 需要动态的创建xml节点并添加,实现方法如下: var NewItem:IXMLDOMElement; NewItem:=ConfigDoc.createElement('ite ...

  10. C# 查农历 阴历 阳历 公历 节假日

    原文:C# 查农历 阴历 阳历 公历 节假日 using System;using System.Collections.Generic;using System.Text; namespace ca ...