最近听了一些关于Memory Leak(内存泄漏)的seminar,感觉有些收获,所以留个记录,并share给朋友。

1 什么是Memory Leak。 Memory Leak是指由于错误或不完备的代码造成一些声明的对象实例长期占有内存空间,不能回收。Memory Leak会造成系统性能下降,或造成系统错误。

2 Memory存储模式 我们通常写的C++或Java Code在内存里边的存储状况概如下图。

idea使用memory analyzer idea memory leak_java

简单的说,一般局部变量存储于Stack中,以提高运行问速度。而New出来的变量则将引用信息或指针存储在Stack中,而将对象本身存储与Heap区中。

这里感谢俊晓同学看完blog后提供了这如下link,可以让大家更好的理解什么堆啊,栈啊之类的概念。

http://www.builder.com.cn/2007/1010/544483.shtml

3 编码产生Memory Leak的原因,及避免   
Memory Leak的原因现归纳出3种,以后要还有,再做补充。  
(1)No Referenced Memory (C++ only)    

Sample 1   a(){    
DKString* s= new DKString();    
 … …   
 … …    
delete s;   
 }        
Sample 2   a(){    
char* buffer = (char*)malloc(64 * sizeof(char);   
 … …   
 … …    
free(buffer);    
}

   C++里边生成/释放变量的方式有两种,new/delete, malloc()/free()。无论用那种方式生成,最后一定要有释放的动作。
否则,在程序离开变量作用域时,Stack里边的引用会被自动回收,但Heap里的对象实例本身就将成为被永久遗弃在内存里的No Referenced Memory。   
另外需要注意的是, 如果用new生成的,一定要用delete释放;
如果用malloc生成的,一定要用free释放。反之,虽然代码可以通过编译,但是会造成很多潜在问题。

   (2)No free Objects/Pointers (C++ and Java)    
Java比C++方便的地方是Java可以自圾回收已经过期的内存垃圾,GC。所以,Java程序员从不用关心delete还是free的问题。
但是碰到下面这种情况,GC也无能为力,更不要说C++了。    

Sample 3     
String[] sa = new String[9999999];     
for (int i = 0; i < 9999999; i++){       
String s = new String(“adfasdfadsfas…adfasdfa”); //a 1MB size string…      
 sa[i] = s;     
}

这段代码让GC郁闷的是,当循环结束之前,GC永远收不到任何空间。
因为GC只能收集那些过期的变量,可是在sa过期之前,可能OutOfMemory已经发生了。    

  (3)No Limited Storage (C++ and Java)   

Sample 4    … …     
While (true){     
 Vector.add(obj);    
}   
 … …

像 Vector, hashtable, hashmap, map, arraylist and String StringBuffer… …这样的工具类自身没有上限,如果,Developer再不加控制,很容易内存溢出。    
4 如何通过测试发现Memory Leak  
(1)Long Run  
很多时候,微小的Memory Leak不会给我们的系统造成太多的影响,只有当泄露积累到一定程度,问题才会爆发。因此,从理论上说,我们要让代码多次重复的Run从而暴露Memory Leak问题。在我们公司,这种测试叫Long Run。所谓Long Run并不一定说一定要让测试Case跑多么长的时间,而是跑足够的次数。  
(2)特殊Case  
这是一个Tester的经验做法,他们相信developer会在大部分正常的程序逻辑里边考虑和处理Memory Leak问题,但异常条件也许未必,所以,通过适当的临界测试和特殊case测试,亦或能找到Memory Leak的case.     
个人认为,发现并避免Memory Leak,最重要的还是Developer从程序设计、编码的时候就在上游把好质量关。否则,真正到Tester发现并定位Memory Leak问题,代价则相当大。   

5 分析Memory Leak的工具  

工欲善其事,必先利其器。这里会搜及并不断补充一些Memory Leak的分析工具,以满足Tool People的要求。  
(1)Purify    一般For C Code  
(2)Heap Analyzer    
可以For Java Code  
(3)Java Dump    
这牵涉到另一个Topic,希望以后能有所补充。


汗死,把内存溢出和内存泄漏混为一谈了,后面两种情况不属于内存泄漏,而是内存溢出。
内存泄漏通俗的讲是指某个程序申请了某块内存,不用了以后,没有回收,系统以为该内存还是被占用的,因此该内存【相当于】消失了。
而内存溢出则是程序在申请内存时,没有足够的内存空间供其使用。
二者之间的联系是:内存泄漏如果累积最终也会造成内存溢出

 

 

 

内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。

内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。

memory leak会最终会导致out of memory!

内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出。 
内存泄漏是指你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的那块内存你自己也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序。一个盘子用尽各种方法只能装4个果子,你装了5个,结果掉倒地上不能吃了。这就是溢出!
比方说栈,栈满时再做进栈必定产生空间溢出,叫上溢,栈空时再做退栈也产生空间溢出,称为下溢。就是分配的内存不足以放下数据项序列,称为内存溢出. 
   以发生的方式来分类,内存泄漏可以分为4类: 
1. 常发性内存泄漏。
发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。 

2. 偶发性内存泄漏。
发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。
常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。
所以测试环境和测试方法对检测内存泄漏至关重要。 

3. 一次性内存泄漏。
发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。
比如,在类的构造函数中分配内存,在析构函数中却没有释放该内存,所以内存泄漏只会发生一次。 

4. 隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。
严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。
但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。
所以,我们称这类内存泄漏为隐式内存泄漏。 
从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,作为一般的用户,根本感觉不到内存泄漏的存在。
真正有危害的是内存泄漏的堆积,这会最终消耗尽系统所有的内存。
从这个角度来说,一次性内存泄漏并没有什么危害,因为它不会堆积,而隐式内存泄漏危害性则非常大,因为较之于常发性和偶发性内存泄漏它更难被检测到