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博客 ” ,链接:https://blog.itpub.net/69914734/viewspace-2656639/ ,如需转载,请注明出处,否则将追究法律责任。