重载new和delete来检测内存泄漏

1. 简述

内存泄漏属于资源泄漏的一种,百度百科将内存泄漏分为四种:常发性内存泄漏、偶发性内存泄漏、一次性内存泄漏和隐式内存泄漏。
    常发性指:内存泄漏的代码会被多次执行到。偶发性指:内存泄漏的代码只有在特定的条件下才会执行到。一次性指:内存泄漏的代码只会被执行到一次。隐式指:程序在运行中不断的开辟内存,知道程序结束时才释放内存,本质上虽然没有内存泄漏,但是如果这个程序在连续运行很长时间,会耗尽所有内存,导致系统崩溃。
    下面首先介绍内存检测的基本原理,然后给出代码样例,最后说明针对四种内存泄漏进行检测的想法。

2. 基本原理

内存泄漏就是new出来的内存没有通过delete合理的释放掉。new和delete这两个函数就是关键点。可以重载new和delete,每次new中开辟一块内存就用链表把这个内存的信息保存下来,每次用delete删除一块内存就从链表中删除这块内存的记录。
3. 代码样例


  1 #include<iostream>
  2 using namespace std;
  3 //---------------------------------------------------------------
  4 // 内存记录
  5 //---------------------------------------------------------------
  6 class MemInfo {
  7 private:
  8   void* ptr;
  9   const char* file;
 10   unsigned int line;
 11   MemInfo* link;
 12   friend class MemStack;
 13 };
 14 //---------------------------------------------------------------
 15 // 内存记录栈 
 16 //---------------------------------------------------------------
 17 class MemStack {
 18 private:
 19   MemInfo* head;
 20 public:
 21   MemStack():head(NULL) { }
 22   ~MemStack() { 
 23     MemInfo* tmp;
 24     while(head != NULL) {
 25       free(head->ptr); // 释放泄漏的内存 
 26       tmp = head->link;
 27       free(head);
 28       head = tmp;
 29     }
 30   }
 31   void Insert(void* ptr, const char* file, unsigned int line) {
 32     MemInfo* node = (MemInfo*)malloc(sizeof(MemInfo));
 33     node->ptr = ptr; node->file = file; node->line=line;
 34     node->link = head; head = node;    
 35   }
 36   void Delete(void* ptr) {
 37     MemInfo* node = head;
 38     MemInfo* pre = NULL;
 39     while(node != NULL && node->ptr!=ptr) {
 40       pre = node;
 41       node = node->link;
 42     }
 43     if(node == NULL)
 44       cout << "删除一个没有开辟的内存" << endl;
 45     else {
 46       if(pre == NULL) // 删除的是head 
 47         head = node->link;
 48       else 
 49         pre->link = node->link;
 50       free(node);
 51     }
 52   }
 53   void Print() {
 54     if(head == NULL) {
 55       cout << "内存都释放掉了" << endl; 
 56       return;
 57     }
 58     cout << "有内存泄露出现" << endl; 
 59     MemInfo* node = head;    
 60     while(node != NULL) {
 61       cout << "文件名: " << node->file << " , " << "行数: " << node->line << " , "
 62         << "地址: " << node->ptr << endl; 
 63       node = node->link;
 64     }
 65   }
 66 };
 67 //---------------------------------------------------------------
 68 // 全局对象 mem_stack记录开辟的内存 
 69 //---------------------------------------------------------------
 70 MemStack mem_stack;
 71 //---------------------------------------------------------------
 72 // 重载new,new[],delete,delete[] 
 73 //---------------------------------------------------------------
 74 void* operator new(size_t size, const char* file, unsigned int line) {
 75   void* ptr = malloc(size);
 76   mem_stack.Insert(ptr, file, line);
 77   return ptr;
 78 }
 79 void* operator new[](size_t size, const char* file, unsigned int line) {
 80   return operator new(size, file, line); // 不能用new 
 81 }
 82 void operator delete(void* ptr) {
 83   free(ptr);
 84   mem_stack.Delete(ptr);
 85 }
 86 void operator delete[](void* ptr) {
 87   operator delete(ptr);
 88 }
 89 //---------------------------------------------------------------
 90 // 使用宏将带测试代码中的new和delte替换为重载的new和delete 
 91 //---------------------------------------------------------------
 92 #define new new(__FILE__,__LINE__)
 93 //---------------------------------------------------------------
 94 // 待测试代码 
 95 //---------------------------------------------------------------
 96 void bad_code() {
 97   int *p = new int;
 98   char *q = new char[5];
 99   delete []q;
100 } 
101 
102 void good_code() {
103   int *p = new int;
104   char *q = new char[5];
105   delete p;
106   delete []q;
107 } 
108 //---------------------------------------------------------------
109 // 测试过程 
110 //---------------------------------------------------------------
111 int main() {
112   good_code();
113   bad_code();
114   mem_stack.Print();
115   system("PAUSE");
116   return 0;
117 }

输出结果为:
    
    可见97行开辟的int,没有delete掉,输出结果也显示为97行。

4. 代码说明

4.1 关于new的参数问题。
    对于new int,编译器会解释为new(sizeof(int)),对于new int[5],编译器会解释为new(sizeof(int)*5)。因此使用宏定义预编译后,new int就变为new (__FILE__,__LINE__) int,编译器会解释为new(sizeof(int), __FILE__,__LINE__)。

4.2 关于MemStack
    MemStack内部也是一个链表结构,注意内部实现不能使用new和delete,只能使用malloc和free来实现链表,因为待测代码中的重载new和delete中调用了MemStack的insert和delete函数,如果insert和delete函数也调用重载后的new和delete的话,会构成死循环的,所以直接使用free和malloc比较好。
    MemStack中的析构函数,会释放掉泄漏掉的内存。

5. 使用思考

对于常发性和一次性的内存泄漏代码,直接放入测试就好了。对于偶发性的内存泄漏代码,只要满足特定条件,那么也就转化为常发性或者一次性的内存泄漏了。
    对于隐式内存泄漏,由于程序是在很长一段时间之后导致内存耗尽,我们需要长时间观察,每隔一段时间比较一下内存的使用量,如果在一个较长的时间内,内存使用量持续增加,那么可以考虑是内存泄漏。不过调试起来可能会比较麻烦,还是需要重新审视程序设计的。

6. 参考

百度百科_内存泄漏:介绍内存泄漏的基本分类。
    http://baike.baidu.com/view/714962.htm
    如何检查内存泄漏-重载new和delete:十分生动的说明。
    http://www.cppblog.com/dawnbreak/articles/76223.html
    一个跨平台的C++内存泄漏检测器:十分专业化的讲解和实现。
    http://www.ibm.com/developerworks/cn/linux/l-mleak2/index.html

重载new和delete来检测内存泄漏的更多相关文章

  1. 如何在linux下检测内存泄漏

    之前的文章应用 Valgrind 发现 Linux 程序的内存问题中介绍了利用Linux系统工具valgrind检测内存泄露的简单用法,本文实现了一个检测内存泄露的工具,包括了原理说明以及实现细节. ...

  2. Vc 检测内存泄漏

    启用内存泄漏检测 检测内存泄漏是 C/c + + 调试器和 C 运行时库 (CRT) 的主要工具调试堆函数. 若要启用调试堆的所有函数,在 c + + 程序中,按以下顺序包含以下语句: C++复制 # ...

  3. 如何在linux下检测内存泄漏(转)

    本文转自:http://www.ibm.com/developerworks/cn/linux/l-mleak/ 本文针对 linux 下的 C++ 程序的内存泄漏的检测方法及其实现进行探讨.其中包括 ...

  4. Qt creator 搭配 valgrind 检测内存泄漏

    继上次重载operator new检测内存泄漏失败之后,妥协了.决定不管是否是准确指明哪一行代码出现内存泄漏,只要告诉我是否有泄漏就行了,这样就没有new替换的问题.在开发中,总是一个个小功能的开发. ...

  5. 面向开发的内存调试神器,如何使用ASAN检测内存泄漏、堆栈溢出等问题

    GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 目录 介绍 如何使用 ASAN 检测内存泄漏 检测悬空指针访问 检测堆溢出 C++ 中的new/delete不匹配 检测栈 ...

  6. Android性能优化之利用LeakCanary检测内存泄漏及解决办法

    前言: 最近公司C轮融资成功了,移动团队准备扩大一下,需要招聘Android开发工程师,陆陆续续面试了几位Android应聘者,面试过程中聊到性能优化中如何避免内存泄漏问题时,很少有人全面的回答上来. ...

  7. 使用Visual Leak Detector检测内存泄漏[转]

      1.初识Visual Leak Detector 灵活自由是C/C++语言的一大特色,而这也为C/C++程序员出了一个难题.当程序越来越复杂时,内存的管理也会变得越加复杂,稍有不慎就会出现内存问题 ...

  8. monkey检测内存泄漏

    monkey中检查内存泄漏,实际上是对一个操作多次操作后看内存情况,内存泄漏具体的原理可百度,现在我们梳理检测内存泄漏的方法: 测试前你需要安装: 1.MAT分析工具 2.使用工具事实监控内存指标,现 ...

  9. VC使用CRT调试功能来检测内存泄漏

    信息来源:csdn     C/C++ 编程语言的最强大功能之一便是其动态分配和释放内存,但是中国有句古话:“最大的长处也可能成为最大的弱点”,那么 C/C++ 应用程序正好印证了这句话.在 C/C+ ...

随机推荐

  1. NSString的几种常用方法—韩俊强博…

    要把 "2011-11-29" 改写成 "2011/11/29"一开始想用ios的时间格式,后来用NSString的方法搞定. 1.创建NSString字符串 ...

  2. [Java]数组排序-选择排序 冒泡排序 插入排序

    1 选择排序  原理:a 将数组中的每个元素,与第一个元素比较          如果这个元素小于第一个元素, 就将这个         两个元素交换.       b 每轮使用a的规则, 可以选择出 ...

  3. Android ViewManger解析 从ViewRoot 源码分析invalidate

    转载请标明出处:http://blog.csdn.net/sk719887916/article/details/48443429,作者:skay     通过学习了AndroidUI之绘图机基础知道 ...

  4. python屏幕的交互(读取输出信息)input,raw_input的区别

    >>> input("your name?") your name?sam Traceback (most recent call last):   File & ...

  5. 纯命令提交代码到git仓库(教你怎么装逼)

    如果不喜欢用命令的请点链接:http://blog.csdn.net/xiangzhihong8/article/details/50715427 我这里用纯命令,主要是因为这两天不知道什么原因,ba ...

  6. Unity3D学习笔记(五)C#与JavaScript组件访问的比较

    由于之前用JavaScript用的比较多,因此总是想用以前的方法来访问组件,却屡遭失败,经过查阅资料发现,二者存在较大的不同. 下面以调用3D Text组件HurtValue为例,来比较二者的不同 J ...

  7. hadoop学习视频

    杨尚川的视频 http://www.tudou.com/plcover/EvJCo2zl9hQ/ 酷6视频 http://v.ku6.com/show/8PkgqGcarHKndyP3rl_pUw.. ...

  8. sqlite相关使用

    一.SQLite简介   SQLite是一款轻型的数据库,是遵守ACID的关联式数据库管理系统,它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能 ...

  9. iOS开发讲解SDWebImage,你真的会用吗?

    SDWebImage作为目前最受欢迎的图片下载第三方框架,使用率很高.但是你真的会用吗?本文接下来将通过例子分析如何合理使用SDWebImage. 使用场景:自定义的UITableViewCell上有 ...

  10. 根据Facebook内存的管理使用,浅谈在iOS上自动检测内存泄漏问题

    分装库下载:https://github.com/facebook/FBMemoryProfiler FBMemoryProfiler类库使用教程:http://ifujun.com/fbmemory ...