軟體測試學習教程——過載new或delete來檢測記憶體洩漏

千鋒教育官方發表於2019-09-10

 1. 簡述

 

  在日常生活中,例如電腦,還有我們大家每天離不開的手機,用的時間久了,就會發現執行速度越來越快,用方言講,就是卡的不行啦, 電腦或手機出現這種情況,大家勢必會感到非常煩躁,有那種摔電腦或手機的衝動。

 

  記憶體洩漏屬於資源洩漏的一種,百度百科將記憶體洩漏分為四種:常發性記憶體洩漏、偶發性記憶體洩漏、一次性記憶體洩漏和隱式記憶體洩漏。

 

  常發性指:記憶體洩漏的程式碼會被多次執行到。偶發性指:記憶體洩漏的程式碼只有在特定的條件下才會執行到。一次性指:記憶體洩漏的程式碼只會被執行到一次。隱式指:程式在執行中不斷的開闢記憶體,知道程式結束時才釋放記憶體,本質上雖然沒有記憶體洩漏,但是如果這個程式在連續執行很長時間,會耗盡所有記憶體,導致系統崩潰。

 

  下面首先介紹記憶體檢測的基本原理,然後給出程式碼樣例,最後說明針對四種記憶體洩漏進行檢測的想法。

 

  2. 基本原理

 

  記憶體洩漏就是new 出來的記憶體沒有透過 delete 合理的釋放掉。 new delete 這兩個函式就是關鍵點。可以過載 new delete ,每次 new 中開闢一塊記憶體就用連結串列把這個記憶體的資訊儲存下來,每次用 delete 刪除一塊記憶體就從連結串列中刪除這塊記憶體的記錄。

 

  #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}

 

  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. 使用思考

 

  對於常發性和一次性的記憶體洩漏程式碼,直接放入測試就好了。對於偶發性的記憶體洩漏程式碼,只要滿足特定條件,那麼也就轉化為常發性或者一次性的記憶體洩漏了。對於隱式記憶體洩漏,由於程式是在很長一段時間之後導致記憶體耗盡,我們需要長時間觀察,每隔一段時間比較一下記憶體的使用量,如果在一個較長的時間內,記憶體使用量持續增加,那麼可以考慮是記憶體洩漏。不過除錯起來可能會比較麻煩,還是需要重新審視程式設計的。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69914734/viewspace-2656639/,如需轉載,請註明出處,否則將追究法律責任。

相關文章