軟體測試學習教程——過載new或delete來檢測記憶體洩漏
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/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- iOS檢測記憶體洩漏iOS記憶體
- 軟體測試學習教程——WEB測試之JS記憶體WebJS記憶體
- 如何在 Linux 下檢測記憶體洩漏Linux記憶體
- Android記憶體洩漏檢測與修復技巧Android記憶體
- 記憶體(new delete )記憶體delete
- 從預設解構函式學習c++,new,delete,記憶體洩漏,野指標函式C++delete記憶體指標
- 使用VLD進行記憶體洩漏檢測(release + debug)記憶體
- C++--問題27--如何檢測記憶體洩漏C++記憶體
- 軟體測試學習教程—軟體測試質量
- 軟體測試學習教程—軟體測試基本知識
- 軟體測試學習教程—迴歸測試
- 程式碼安全測試第十一期:記憶體洩漏漏洞記憶體
- Android 記憶體洩漏檢測工具 LeakCanary(Kotlin版)的實現原理Android記憶體Kotlin
- 軟體測試學習教程—軟體測試基礎理論五
- 軟體測試學習教程—軟體測試基礎理論六
- 軟體測試學習教程—軟體測試基礎理論四
- 軟體測試學習教程—軟體測試基礎理論三
- js記憶體洩漏JS記憶體
- Android記憶體洩漏Android記憶體
- Android 記憶體洩漏Android記憶體
- jvm 記憶體洩漏JVM記憶體
- Java記憶體洩漏Java記憶體
- 分析記憶體洩漏和goroutine洩漏記憶體Go
- 【軟體測試】學習筆記筆記
- 這可能是,Flutter 中最“強悍”的記憶體洩漏檢測方案......Flutter記憶體
- Node.js 應用的記憶體洩漏問題的檢測方法Node.js記憶體
- 軟體測試學習教程——JDBC配置JDBC
- 軟體測試學習教程——LoadRunner實現介面測試
- C程式記憶體洩露檢測工具——ValgrindC程式記憶體洩露
- 如何檢查Javascript中的記憶體洩漏JavaScript記憶體
- 基於Android Studio的記憶體洩漏檢測與解決全攻略Android記憶體
- 【記憶體洩漏和記憶體溢位】JavaScript之深入淺出理解記憶體洩漏和記憶體溢位記憶體溢位JavaScript
- 軟體測試學習 ——五種軟體測試模型模型
- 記憶體洩漏的原因記憶體
- valgrind 記憶體洩漏分析記憶體
- JVM——記憶體洩漏與記憶體溢位JVM記憶體溢位
- 2020版軟體測試全新4.0教程震撼來襲,不一樣的軟體測試學習路
- 前端面試查漏補缺--(十三) 記憶體洩漏前端面試記憶體