内存泄露

来源:互联网 发布:10月经济数据全面回落 编辑:程序博客网 时间:2024/03/29 16:56

转自:http://blog.csdn.net/cctt_1/archive/2010/07/22/5755632.aspx

 

 

 

相信几乎每个人都遇到过memory leak的问题。解决方法各不相同。

1。防止内存泄露

例如c++中使用auto_ptr, java中自己的垃圾回收。对于纯java, python的语言编写的东西,memory leak一般不是啥米的问题,这些语言最严重的问题是内存不足。这些java要处理内存不足就-Xmx1024m或者开更大的内存,或者闲得无聊的时候调调gc. python的解决方法差不多,不过它可以显式delete. 另外听说python调用django时会有内存分配不足的问题,原因在于django这鬼东西在debug==true时没有对sql查询进行释放。下面接着过去的话题扯下c++/c这些需要自己care的语言。 c++中有auto_ptr这东东算是可以,因为遵循RAII(resource acquirement is initial 不知道自己英文拼对了没有), 也就是说:在类初始化的时候申请资源,在类析构的时候释放资源。另外注意析构函数是virtual的。为啥米呢?因为要多态调用析构父类。如果自己写的类不是那么标准,喜欢到处申请资源,到处释放资源,还是建议使用auto_ptr这个类,它在<memory>中。其实auto_ptr类似于下面的代码:

 

view plaincopy to clipboardprint?
  1. template<class T>  
  2. class simple_auto_ptr{  
  3. private:  
  4.    T* point;  
  5. public:  
  6.    explicit simple_auto_ptr(T* point):point(point){}  
  7.    virtual ~simple_auto_ptr(){delete point;}  
  8. };  
  9. 调用代码例如simple_auto_ptr<int> a(new int(3));  

 


这东东的作用在于简化下面的代码:

 

view plaincopy to clipboardprint?
  1. void foo(){   
  2.     T* a = new T();  
  3.     try{   
  4.          throw something;  
  5.     }catch(something){   
  6.         if(NULL!=a){  
  7.             delete a;  
  8.             a=NULL;  
  9.          }  
  10.     }   
  11.     if(NULL != a)  
  12.        delete a;  
  13. }   
  14. 使用auto_ptr 将上面的简化为   
  15. void foo(){  
  16.     auto_ptr<T> a(new T());   
  17.     throw something;   
  18. }  

 

可以说还是有些用处的..不过不能用于申请数组...除非你想申请auto_ptr的数组。另外传说不能用于容器类。所以为了不让内存泄露,还是需要谨慎小心。

2。监测内存泄露,使用监测内存泄露的小工具

另外有一些监测工具可以帮你检查一部分内存泄露的问题。有些工具其实就是替换了free/malloc/realloc/calloc 或者是 new/delete.采用宏定义就可以轻松做到:

 

view plaincopy to clipboardprint?
  1. #define new MY_NEW  
  2. #define MY_NEW my_new  

 

然后就可以截获new/delete的函数调用,至于你想再多做一些事情,就看你宏定义和你函数的具体写法了。然后就如同左右括号匹配一样。当然往往不是那么简单,例如类中的一些指针有还有一些内存检测是需要你传入指针进去,然后catch指针做些监测工作。

但有些memory leak的问题很难被监测到,或者有时候会被误报。比如某个指针在函数中申请空间并传出,然后在外面进行n次转换,并进行加加见见运算,最后变为另外一个指针。除非检测工具做了名字的转换,可以进行数学运算,比如int * a = new int[20]; int * c = ++a; delete [] --c;当然这里只是一个模拟,没有人会这样写代码。但是真正的程序中很有可能代码简化后就是这样的情况。当然进行dynamic_cast的情况更加复杂,还需要判断析构函数是否是虚函数等问题。

3。断言法

测试程序正确性很难监测出内存泄露的情况。

下面以链表为例:list_insert()时原List比现List少1个元素,list_delete()时,原List比现List多一个元素。

 

view plaincopy to clipboardprint?
  1. List * l = new List();  
  2. size_t old = l->size();   
  3. l->list_insert(some elements);   
  4. size_t new = l->size();   
  5. assert(new-old == 1);   
  6. old = l->size();   
  7. l->list_delete(some elements);   
  8. new = l->size();   
  9. assert(old-new == 1);  
  10. // 另外必须保证list的size其实和具体的值无关,应该是遍历后的结果  

 

 即使这里的size变化是正确的,这也依旧不能保证list_delete()中调用了合适的delete 方法,有可能没有进行delete, 或者进行了节点的delete,但是组成链表中的节点的析构函数没有写好,也会造成内存泄露。个人就遇到PG中删除链表函数的问题。原链表长度的确是减小了。但是链表节点的内存的确没有释放。

4。模拟法

这个方法其实很好用,一般在已经确定可能是某一个大模块的问题,但是不清楚内部可能的路径,也有可能是路径太过复杂。这里就可以模拟一下。化简各种流程,仅仅调用申请释放资源的几个主要函数。然后逐一排除各个主要函数的可能性。例如那个链表,如果起初的业务流程有许多复杂的链表操作,但是申请释放空间的地方其实就是list_insert()和list_delete(),这就可以仅仅是调用几千次list_new(),list_delete()看看情况.内存泄露就会明显显现。

原创粉丝点击