zoukankan      html  css  js  c++  java
  • 重载new和delete来检测内存泄漏

    重载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

  • 相关阅读:
    preventDefault
    YII2中的Html助手和Request组件
    yii2 输出json的方法
    FormData异步上传
    通过Ajax方式上传文件,使用FormData进行Ajax请求
    Yii设置Cache缓存的方法
    exif_imagetype() 函数在linux下的php中不存在
    Thinkphp整合各个功能
    PHP cURL库函数抓取页面内容
    javascript实现打印功能
  • 原文地址:https://www.cnblogs.com/xxiaoye/p/3985838.html
Copyright © 2011-2022 走看看