最近听了一些关于Memory Leak(内存泄漏)的seminar,感觉有些收获,所以留个记录,并share给朋友。
1 什么是Memory Leak。
Memory Leak是指由于错误或不完备的代码造成一些声明的对象实例长期占有内存空间,不能回收。Memory Leak会造成系统性能下降,或造成系统错误。
2 Memory存储模式
我们通常写的C++或Java Code在内存里边的存储状况概如下图。
简单的说,一般局部变量存储于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,希望以后能有所补充。