重载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   * =   new   int ;
 98     char   * =   new   char [ 5 ];
 99    delete []q;
100 
101 
102  void  good_code() {
103     int   * =   new   int ;
104     char   * =   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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值