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

  • 相关阅读:
    The Quad
    将OrCAD Capture CIS的设计文件(.dsn)导入到PADS Logic VX.2.3
    OrCAD Capture CIS 16.6 将版本16.6的设计文件另存为版本16.2的设计文件
    Eclipse IDE 添加jar包到Java工程中
    PADS Logic VX.2.3 修改软件界面语言
    切换Allegro PCB Editor
    Allegro PCB Design GXL (legacy) 将brd文件另存为低版本文件
    Allegro PCB Design GXL (legacy) 设置自动保存brd文件
    Could not create an acl object: Role '16'
    windows 下apache开启FastCGI
  • 原文地址:https://www.cnblogs.com/xxiaoye/p/3985838.html
Copyright © 2020-2023  润新知