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