重載new和delete來檢測內存泄漏 重載new和delete來檢測內存泄漏

重載new和delete來檢測內存泄漏

1. 簡述html

    內存泄漏屬於資源泄漏的一種,百度百科將內存泄漏分爲四種:常發性內存泄漏、偶發性內存泄漏、一次性內存泄漏和隱式內存泄漏。
    常發性指:內存泄漏的代碼會被屢次執行到。偶發性指:內存泄漏的代碼只有在特定的條件下才會執行到。一次性指:內存泄漏的代碼只會被執行到一次。隱式指:程序在運行中不斷的開闢內存,知道程序結束時才釋放內存,本質上雖然沒有內存泄漏,可是若是這個程序在連續運行很長時間,會耗盡全部內存,致使系統崩潰。
    下面首先介紹內存檢測的基本原理,而後給出代碼樣例,最後說明針對四種內存泄漏進行檢測的想法。node

2. 基本原理linux

    內存泄漏就是new出來的內存沒有經過delete合理的釋放掉。new和delete這兩個函數就是關鍵點。能夠重載new和delete,每次new中開闢一塊內存就用鏈表把這個內存的信息保存下來,每次用delete刪除一塊內存就從鏈表中刪除這塊內存的記錄。
3. 代碼樣例ios

複製代碼
  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. 代碼說明post

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中的析構函數,會釋放掉泄漏掉的內存。url

5. 使用思考spa

    對於常發性和一次性的內存泄漏代碼,直接放入測試就行了。對於偶發性的內存泄漏代碼,只要知足特定條件,那麼也就轉化爲常發性或者一次性的內存泄漏了。
    對於隱式內存泄漏,因爲程序是在很長一段時間以後致使內存耗盡,咱們須要長時間觀察,每隔一段時間比較一下內存的使用量,若是在一個較長的時間內,內存使用量持續增長,那麼能夠考慮是內存泄漏。不過調試起來可能會比較麻煩,仍是須要從新審視程序設計的。設計

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

相關文章
相關標籤/搜索