代碼測試以內存泄露

http://blog.csdn.net/feixiaoxing/article/details/6746335node

 

在 咱們我的編程的過程中,內存泄露雖然不會像內存溢出那樣形成各類莫名奇妙的問題,可是它的危害也是不可忽視的。一方面,內存的泄露致使咱們的軟件在運行 過程當中佔用了愈來愈多的內存,佔有資源而又得不到及時清理,這會致使咱們程序的效率愈來愈低;另外一方面,它會影響咱們用戶的體驗,失去市場的競爭能力。編程

    常見的內存泄露是這樣的:緩存

 

  1. void process(int size)  
  2. {  
  3.     char* pData = (char*)malloc(size);  
  4.   
  5.     /* other code  */  
  6.       
  7.     return/* forget to free pData */  
  8. }  

    如上圖所示,咱們在函數process的處理過程當中,每一次都須要對內存進行申請,可是在函數結束的時候卻沒有進行釋放。若是這樣的一段代碼出如今業務 側,那麼後果是不可思議的。舉個例子來講,若是咱們服務器每秒鐘須要接受100個用戶的併發訪問,每一個用戶過來的數據,咱們都須要本地申請內存從新保存一 份。處理結束以後,若是內存沒有獲得很好地釋放,就會致使咱們服務器可用的物理內存愈來愈少。一旦達到某一個臨界點以後,操做系統不得不經過內外存的調度 來知足咱們申請新內存的需求,這在另外一方面來說又會下降服務器服務的質量。服務器

    內存泄露的危害是不言而喻的,可是查找內存泄露倒是一件苦難並且複雜的工做。咱們都知道,解決bug是一件很是簡單的事情,可是尋找bug的出處倒是一 件很是吃力的事情。所以,咱們有必要在本身編寫代碼的時候,就把查找內存泄露的工做放在很重要的位置上面。那麼有沒有什麼辦法來解決這一問題呢?數據結構

    我想要作到解決內存泄露,必須作到下面兩個方面:併發

    (1)必須記錄內存在哪一個函數申請的,具體文件的行數是多少函數

    (2)內存應該何時被釋放測試

   要完成第1個條件其實並不困難。咱們能夠用節點的方法記錄咱們申請的內存:spa

    a)設置節點的數據結構操作系統

 

  1. typedef struct _MEMORY_NODE  
  2. {  
  3.     char functionName[64];  
  4.     int line;  
  5.     void* pAddress;  
  6.     struct _MEMORY_NODE* next;  
  7.   
  8. }MEMORY_NODE;  

    其中 functionName記錄函數名稱,line記錄行數, pAddress記錄分配的地址, next記錄下一個內存節點。

    

    b)修改內存的分配函數

    對業務側的malloc進行函數修改,添加下面一句宏語句

    #define malloc(param)  MemoryMalloc(__FUNCTION__, __LINE__, param)

    在樁函數側書寫下面的代碼

 

  1. void* MemoryMalloc(const char* name, int line, int size)  
  2. {  
  3.     void* pData = (void*)malloc(size);  
  4.     MEMORY_NODE* pMemNode = NULL;  
  5.     if(NULL == pData) return NULL;  
  6.     memset((char*)pData, 0, size);  
  7.   
  8.     pMemNode = (MEMORY_NODE*)malloc(sizeof(MEMORY_NODE));  
  9.     if(NULL == pMemNode){  
  10.         free(pData);  
  11.         return NULL;  
  12.     }  
  13.     memset((char*)pMemNode, 0, sizeof(MEMORY_NODE));  
  14.     memmove(pMemNode->functionName, name, strlen(name));  
  15.     pMemNode->line = line;  
  16.     pMemNode->pAddress = pData;  
  17.     pMemNode->next = NULL;  
  18.     add_memory_node(pMemNode);  
  19.   
  20.     return pData;  
  21. }  


    內存的分配過程當中還涉及到了節點的添加,因此咱們還須要添加下面的代碼

 

  1. static MEMORY_NODE* gMemNode = NULL;  
  2.   
  3. void add_memory_node(MEMORY_NODE* pMemNode)  
  4. {  
  5.     MEMORY_NODE* pNode = gMemNode;  
  6.     if(NULL == pMemNode) return;  
  7.     if(NULL == gMemNode){  
  8.         gMemNode = pMemNode;  
  9.         return;  
  10.     }  
  11.   
  12.     while(NULL != pNode->next){  
  13.         pNode = pNode->next;  
  14.     }  
  15.     pNode->next = pMemNode;  
  16.     return;  
  17. }  

    文中gMemNode表示全部內存節點的根節點,咱們每增長一次malloc過程就會對內存節點進行記錄。在記錄過程當中,咱們還會記錄調用malloc的函數名稱和具體文件行數,這主要是爲了方便咱們在後面進行故障定位的時候更好地查找。

   完成了第一個條件以後,咱們就要對第二個條件進行完成。

   a)內存何時釋放,這取決於咱們在函數中是怎麼實現的,可是咱們在編寫測試用例的時候倒是應該知道內存釋放沒有,好比說若是測試用例所有結束了,咱們有理由相信assert(gMemNode == NULL)這應該是恆等於真的。

    b)內存釋放的時候,咱們應該作些什麼?和節點的添加同樣,咱們在內存釋放的時候須要free指定的內存,free節點,free節點的內存,下面就是在釋放的時候咱們須要進行的操做

 

    對業務側的free函數進行修改,添加下面一句宏代碼,

    #define free(param)      MemoryFree(param)

 

    在樁函數側輸入下面的代碼:

 

  1. void MemoryFree(void* pAddress)  
  2. {  
  3.     if(NULL == pAddress) return;  
  4.     delete_memory_node(pAddress);  
  5.     free(pAddress);  
  6. }  


    在刪除內存的時候,須要刪除節點,刪除節點的內存

 

  1. void delete_memory_node(void* pAddress)  
  2. {  
  3.     MEMORY_NODE* pHead = gMemNode;  
  4.     MEMORY_NODE* pMemNode = gMemNode;  
  5.     while(NULL != pMemNode){  
  6.         if(pAddress == pMemNode->pAddress)  
  7.             break;  
  8.         pMemNode = pMemNode->next;  
  9.     }  
  10.     if(NULL == pMemNode) {  
  11.         assert(1 == 0);  
  12.         return;  
  13.     }  
  14.   
  15.     while(pMemNode != pHead->next){  
  16.         pHead = pHead->next;  
  17.     }  
  18.   
  19.     if(pMemNode == gMemNode){  
  20.         gMemNode = gMemNode->next;  
  21.     }else{  
  22.         pHead->next = pMemNode->next;  
  23.     }  
  24.     free(pMemNode);  
  25.     return;  
  26. }  


    有了上面一小段代碼的幫助,咱們在編寫測試用例的時候,就能夠在函數執行後,經過判斷內存節點是否爲空的方法判斷內存是否已經釋放。若是內存沒有釋放,咱們還能經過節點的信息幫助咱們是哪裏發生了錯誤,可是這個方法還有兩個缺點:

    (1)沒有考慮緩存的狀況,好多內存分配了以後並不會在函數中立刻釋放,而是放在緩存池中等待下一次調用,這就須要咱們準確把握和判斷了。

    (2)代碼中節點刪除和添加的時候沒有考慮多進程的情形,應該考慮用一個互斥鎖或者是信號量加以保護。

相關文章
相關標籤/搜索