1.什麼是記憶體泄漏(Memory Leak)? 簡單地說就是申請了一塊記憶體空間,使用完畢後沒有釋放掉。它的一般表現方式是程式運行時間越長,占用記憶體越多,最終用盡全部記憶體,整個系統崩潰。由程式申請的一塊記憶體,且沒有任何一個指針指向它,那麼這塊記憶體就泄露了。 2.記憶體泄漏的危害性 從用戶使用程式的角度來 ...
1.什麼是記憶體泄漏(Memory Leak)?
簡單地說就是申請了一塊記憶體空間,使用完畢後沒有釋放掉。它的一般表現方式是程式運行時間越長,占用記憶體越多,最終用盡全部記憶體,整個系統崩潰。由程式申請的一塊記憶體,且沒有任何一個指針指向它,那麼這塊記憶體就泄露了。2.記憶體泄漏的危害性
從用戶使用程式的角度來看,記憶體泄漏本身不會產生什麼危害,作為一般的用戶,根本感覺不到記憶體泄漏的存在。真正有危害的是記憶體泄漏的堆積,這會最終消耗盡系統所有的記憶體。主要有以下幾種表現形式:
1)cpu資源耗盡:估計是機器沒有反應了,鍵盤,滑鼠,以及網路等等。這個在windows上經常看見,特別是中了毒。 2)進程id耗盡:沒法創建新的進程了,串口或者telnet都沒法創建了。 3)硬碟耗盡: 機器要死了,交換記憶體沒法用,日誌也沒法用了,死是很正常的。在我們寫程式的時候,一般會使用malloc,realloc,new等函數從堆中分配到一塊記憶體,使用完後,程式必須負責相應的調用free或delete釋放該記憶體塊,否則,這塊記憶體就不能被再次使用,我們就說這塊記憶體泄漏了。如果要避免這個問題,還是要從代碼上入手,良好的編碼習慣和規範,是避免錯誤的不二法門。
3.如何檢測記憶體泄漏?
第一:良好的編碼習慣,儘量在涉及記憶體的程式段,檢測出記憶體泄露。當程式穩定之後,在來檢測記憶體泄露時,無疑增加了排除的困難和複雜度。使用了記憶體分配的函數,一旦使用完畢,要記得要使用其相應的函數釋放掉。
Heap memory:
malloc\realloc ------ free
new \new[] ---------- delete \delete[]
GlobalAlloc------------GlobalFree
要特別註意數組對象的記憶體泄漏
MyPointEX *pointArray =new MyPointEX [100];
其刪除形式為:
delete []pointArray ;
第二:將分配的記憶體的指針以鏈表的形式自行管理,使用完畢之後從鏈表中刪除,程式結束時可檢查改鏈表。
第三:Boost 中的smart pointer。
第四:一些常見的工具插件,如ccmalloc、Dmalloc、Leaky等等。
4.代碼示例
我主要想結合代碼講講第二個方法,設計思想其實很簡單,用到STL中的list。可能有人要問,為什麼不用vector呢?
list和vector的區別如下:
vector為存儲的對象分配一塊連續的地址空間,因此對vector中的元素隨機訪問效率很高。在vecotor中插入或者刪除某個元素,需要將現有元素進行複製,移動。如果vector中存儲的對象很大,或者構造函數複雜,則在對現有元素進行拷貝時開銷較大,因為拷貝對象要調用拷貝構造函數。對於簡單的小對象,vector的效率優於list。vector在每次擴張容量的時候,將容量擴展2倍,這樣對於小對象來說,效率是很高的。
list中的對象是離散存儲的,隨機訪問某個元素需要遍歷list。在list中插入元素,尤其是在首尾插入元素,效率很高,
只需要改變元素的指針。
vector適用:對象數量變化少,簡單對象,隨機訪問元素頻繁
list適用: 對象數量變化大,對象複雜,插入和刪除頻繁
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <list> using namespace std; const int nMaxSize = 26; struct Node { int count; Node *next[nMaxSize]; }; Node *root = NULL; list <Node *> nodeMemory; void TreeCreate() { root = (Node *)malloc(sizeof(Node)); for (int i = 0; i < nMaxSize; ++i) { root->next[i] = NULL; } } void TreeInsert(char *pStr) { int i, j, len = strlen(pStr); Node *p = root; Node *q = NULL; for (i = 0; i < len; ++i) { int id = pStr[i] - 'a'; if (p->next[id] == NULL) { q = (Node *)malloc(sizeof(Node)); if(q != NULL) nodeMemory.push_back(q); q->count = 0; for (j = 0; j < nMaxSize; ++j) { q->next[j] = NULL; } p->next[id] = q; } p->next[id]->count++; p = p->next[id]; } } int TreeQuery(char *pStr) { int i, len = strlen(pStr); int id = 0; Node *p = root; for (i = 0; i < len; ++i) { id = pStr[i] - 'a'; p = p->next[id]; if (p == NULL) return 0; } return p->count; } void TreeDelete(Node *p) { if (p == NULL) return ; for (int i = 0; i < nMaxSize; ++i) { TreeDelete(p->next[i]); } nodeMemory.remove(p); free(p); p = NULL; } int main(int argc, char **argv) { char szBuffer[16]; int res = 0; TreeCreate(); int n = 3; while (n) { gets(szBuffer); if (strlen(szBuffer) == 0) break; TreeInsert(szBuffer); n--; } /* scanf("%s", szBuffer); res = TreeQuery(szBuffer); printf("%d\n", res); */ for(list<Node *>::iterator it = nodeMemory.begin();it != nodeMemory.end();it++) { cout<<*it<<endl; } cout<<nodeMemory.size()<<endl; TreeDelete(root); if(nodeMemory.empty()) cout<<"has delete the tire tree"<<endl; cout<<nodeMemory.size()<<endl; system("pause"); return 0; }
代碼很簡單,list存儲的是指向記憶體空間的指針,每次malloc之後會把這個分配的記憶體指針push到list中,而當free之後,list的就會刪除相應的內容,如果全釋放掉了,則list變為空,從而可以判斷使用後的記憶體是否全部釋放掉了?