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时或者不需要调试内存的时候,仍然使用原生态函数,不影响效率。

 1 #ifdef DEBUG_MEMORY
 2 #define MALLOC MallocDebug(__FILE__, __LINE__, size)
 3 #define FREE FreeDebug(__FILE__, __LINE__, p)
 4 #else
 5 #define MALLOC malloc
 6 #define FREE free
 7 #endif
 8
 9 #ifdef DEBUG_MEMORY
10 #define MEM_OP_MALLOC 1
11 #define MEM_OP_FREE 0
12
13 void LogMemory(const char* file, int line, void* p, int operation, size_t size);
14
15 void* MallocDebug(const char* file, int line, size_t size)
16 {
17     void* p = malloc(size);
18     LogMemory(file, line, p, MEM_OP_MALLOC, size);
19     return p;
20 }
21
22 void FreeDebug(const char* file, int line, void* p)
23 {
24     LogMemory(file, line, p, MEM_OP_FREE, 0);
25      free(p);
26 }
27
28 void LogMemory(const char* file, int line, void* p, int operation, size_t size)
29 {
30     //打印日志(malloc/free、指针、文件名、行号、指针、第几次分配的序号),分配序号可以实现类似与crtdbg的CrtSetBreakAlloc函数的功能
31     //操作为malloc时,向map插入一条记录,增加内存使用大小;
32     //操作为free时,在map中找到记录并删除,减少内存使用大小。
33 }
34
35 void DetectMemoryLeaks()
36 {
37     //打印当前内存管理的map中剩余的没有释放的内存指针、文件名、行号、大小、分配序号
38 }
39
40 #endif
41
42 void Program()
43 {
44     int *pArray = MALLOC(sizeof(int) * 10);
45     FREE(pArray);
46
47 #ifdef DEBUG_MEMORY
48     DetectMemoryLeaks();
49 #endif
50 }

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头文件(伪代码):

 1 //文件名:DebugMemory.h
 2
 3 #ifdef DEBUG_MEMORY
 4 #define NEW(T, ...) DebugNew<T>(__FILE__, __LINE__, __VA_ARGS__)
 5 #define DEL(p) DebugDelete(__FILE__, __LINE__, p)
 6 #define NEW_ARRAY(T, size) DebugNewArray<T>(__FILE__, __LINE__, size)
 7 #define DEL_ARRAY(p) DebugDeleteArray(__FILE__, __LINE__, p)
 8 #else
 9 #define NEW(T, ...) new T(__VA_ARGS__)
10 #define DEL(p) delete(p)
11 #define NEW_ARRAY(T, size) new T[size]
12 #define DEL_ARRAY(p) delete[] p
13 #endif
14
15 #ifdef DEBUG_MEMORY
16
17 template<class T, class... Args>
18 T* DebugNew(const char* file, int line, Args&&... args)
19 {
20     T* p = new T(std::forward<Args>(args)...);
21     //todo:记录操作(new)、指针、文件、行号、分配号
22     return p;
23 }
24
25 template<class T>
26 void DebugDelete(const char* file, int line, T* p)
27 {
28     //todo:记录操作(delete)、指针、文件、行号
29     delete p;
30 }
31
32 template<class T>
33 T* DebugNewArray(const char* file, int line, size_t size)
34 {
35     T* p = new T[size];
36     //todo:记录操作(new[])、指针、文件、行号、分配号
37     return p;
38 }
39
40 template<class T>
41 void DebugDeleteArray(const char* file, int line, T* p)
42 {
43     //todo:记录操作(delete)、指针、文件、行号
44     delete[] p;
45 }
46
47 void DetectMemoryLeaks()
48 {
49     //todo:统计并打印未释放的内存信息
50 }
51
52 #endif

使用DebugMemory.h头文件:

 1 //文件名:main.cpp
 2
 3 #include "DebugMemory.h"
 4
 5 class A
 6 {
 7 public:
 8     A(){}
 9     A(int a, int b):m_a(a), m_b(b){}
10 private:
11     int m_a;
12     int m_b;
13 }
14
15 int main()
16 {
17     A* pA = NEW(A, 1, 2);         //new A(1, 2)
18     DEL(pA);                      //delete pA;
19
20     A* pArray = NEW_ARRAY(A, 10); //new A[10]
21     DEL_ARRAY(pArray);            //delete[] pArray
22
23 #ifdef DEBUG_MEMORY
24     DetectMemoryLeaks();          //内存泄漏检测
25 #endif
26
27     return 0;
28 }

四、方案评价

1.C语言应用程序的内存泄漏解决方案:完美。

2.C++语言应用程序的内存泄漏解决方案

优点:没有改变默认的operator new和operator delete行为,毕竟危险。

优点:实用性通用性强,完全在应用程序员的控制范围内。因为在应用层,不管什么版本都可以检测内存泄漏,不用考虑跨dll调用产生的问题。

不足:写法习惯改变,原来是new A(1,2),要写成NEW(A, 1, 2),如果C++能实现自定义操作符,那么方案就完美了。

原文地址:https://www.cnblogs.com/arider/p/11143672.html

时间: 2024-10-08 09:04:10

C/C++应用程序内存泄漏检查统计方案的相关文章

C++程序内存泄漏检查

一.在windows平台上面:以前我都是用purify,因为没有正版的,很是麻烦. 后来我开始用windows自带的umdh,也很好用:http://support.microsoft.com/kb/268343/en-us摘要一下步骤如下: 1.准备工作: 1 gflags -i <application name> +ust 注:得先把gflags和umdh的路径加入到Path中,默认为:C:\Program Files (x86)\Windows Kits\8.1\Debuggers\x

【转】C++内存泄漏检查心得

摘要:本文简单介绍了C++编程时,大家经常犯得一些内存泄漏方面的编码错误,并给出简单的代码示例.并简要给出了Win32平台下使用检测内存泄漏利器DevPartner BoundsChecker进行检查以发现泄漏代码的详细步骤.值此党的节日,希望对一些迷失在内存泄漏中的同志们有所帮助避免少走弯路.我一直觉得党的党章是完美的,原则是好的,共产主义社会肯定比资本主义财富集中在少数人手里强,只是到了下面执行就有所欠缺了,这次上海闵行封顶房的倒塌正是没有一个良好监督机制的问题,官员参股房地产明显违背政府.

C++内存泄漏检查心得

摘要:本文简单介绍了C++编程时,大家经常犯得一些内存泄漏方面的编码错误,并给出简单的代码示例.并简要给出了Win32平台下使用检测内存泄漏利器DevPartner BoundsChecker进行检查以发现泄漏代码的详细步骤.值此党的节日,希望对一些迷失在内存泄漏中的同志们有所帮助避免少走弯路.我一直觉得党的党章是完美的,原则是好的,共产主义社会肯定比资本主义财富集中在少数人手里强,只是到了下面执行就有所欠缺了,这次上海闵行封顶房的倒塌正是没有一个良好监督机制的问题,官员参股房地产明显违背政府.

vc++ 程序内存泄漏的排查

今天刚学习的,怎样快速准确的查看内存泄漏,马上用到项目中~~~~ 使用_CrtDumpMemoryLeaks,这个函数在crtdbg.h头文件中,一般vc++程序可以直接引用.CrtDumpMemoryLeaks()是显示当前的内存泄漏. 注意"当前"的意思是说当它执行时,所有未销毁的对象均会报内存泄漏.如果是想查程序有没有内存泄漏的话,尽量把这个函数往后面放:如果是检查某个地方的指针内容是否被完全释放,可以写在这个释放之后~~ 当然这个是用在调试模式中,Crt会把前面分配过的内存的文

Unix下C程序内存泄漏检测工具Valgrind安装与使用

Unix下C程序内存泄漏检测工具Valgrind安装与使用 Valgrind是一款用于内存调试.内存泄漏检测以及性能分析的软件开发工具. Valgrind的最初作者是Julian Seward,他于2006年由于在开发Valgrind上的工作获得了第二届Google-O'Reilly开源代码奖. Valgrind遵守GNU通用公共许可证条款,是一款自由软件. 官网 http://www.valgrind.org 下载与安装 #wget http://www.valgrind.org/downlo

Android应用程序内存泄漏介绍

Android应用程序内存泄漏介绍 内存泄漏和内存溢出的区别 内存溢出(out of memory)是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory.比如在我们每个Android程序在运行时系统都会给程序分配一个一定的内存空间,当程序在运行中需要的内存超出这个限制就会报内存溢出(out of memory). 内存泄漏(memory leak)是指程序在申请内存后,无法释放已申请的内存空间.多次内存无法被释放,程序占用的内存会一直增加,直到超过系统的内存限制报内存

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

内存泄漏是c++程序常见的问题了,特别是服务类程序,当系统模块过多或者逻辑复杂后,很难通过代码看出内存泄漏. valgrind是一个开源的,检测c++程序内存泄漏有效工具,编译时加上-g选项可以定位到代码行,同时还检查'野指针',检查malloc与free是否匹配等功能. 参考: 1. Linux下几款C++程序中的内存泄露检查工具 2. linux工具之检测内存泄漏-valgrind 原文地址:https://www.cnblogs.com/embedded-linux/p/9749332.h

Linux下c++程序内存泄漏检测代码范例

Linux下对于程序内存泄漏检测的方法很多,最常用的的莫过于使用valgrind工具.但是valgrind相当于让程序在虚拟机中运行,会带来较大的系统资源开销,还会对程序的运行效率产生较大影响,对于那种资源占用大的程序,如果需要长时间运行才能暴露的泄漏问题,它就显得不太好用. linux下的c++程序中自己实现一个轻量级的泄漏检测代码其实是比较方便的,下面我就给出一个简单的范例,并作简单的说明.当然,我们还是应该提倡使用共享指针,用共享指针自动管理内存可以避免内存泄漏这样的不必要的麻烦. 基本原

linux c程序内存泄漏检测工具-mtrace工具介绍

笔者也是最近去面试被问到怎么做内存泄漏检查,之前都是靠人工屏蔽代码.或者PC-link/KW一类的检查工具进行检查,回来后搜索了下,才知道linux自带的就有mtrace工具. 具体操作步骤如下: 1.在linux下创建test.c文件,编写如下代码: 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 #include <mcheck.h> 6 7 8 int main(