一、内存泄露的概念
内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。(摘抄自百度百科)
内存泄露是由于疏忽或者错误造成程序未能释放已经不再使用的内存。内存泄露并非值内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成内存的浪费。(摘抄自wiki)
二、内存泄露的后果
内存泄漏通常不会直接产生可观察的错误症状,而是逐渐积累,降低系统整体性能,极端的情况下可能使系统崩溃。
内存泄漏会因为减少可用内存的数量从而降低计算机的性能。最终,在最糟糕的情况下,过多的可用内存被分配掉导致全部或部分设备停止正常工作,或者应用程序崩溃。
内存泄漏带来的后果可能是不严重的,有时甚至能够被常规的手段检测出来。在现代操作系统中,一个应用程序使用的常规内存在程序终止时被释放。这表示一个短暂运行的应用程序中的内存泄漏不会导致严重后果。
在以下情况,内存泄漏导致较严重的后果:
- 程序运行后置之不理,并且随着时间的流逝消耗越来越多的内存(比如服务器上的后台任务,尤其是嵌入式系统中的后台任务,这些任务可能被运行后很多年内都置之不理);
- 新的内存被频繁地分配,比如当显示电脑游戏或动画视频画面时;
- 程序能够请求即使在程序终止之后也不会被释放的内存(比如共享内存);
- 泄漏在操作系统内部发生;
- 泄漏在系统关键驱动中发生;
- 内存非常有限,比如在嵌入式系统或便携设备中;
- 当运行于一个程序终止时内存并不自动释放内存的操作系统(比如AmigaOS)之上时。
三、C语言内存泄露示例
1.指针重新赋值
1. #include <stdio.h> 2. 3. int main(void) 4. { 5. char *p = (char *)malloc(10); 6. char *np = (char *)malloc(10); 7. 8. p = np; 9. free(p); 10. free(np); 11. return 0; 12. }
p 和 np 分别被分配了 10 个字节的内存,它们各自的内存如图 1 所示。
当执行p = np之后,这时候,指针变量 p 被 np 指针重新赋值,其结果是 p 以前所指向的内存位置变成了孤立的内存,如图 2 所示。它无法释放,因为没有指向该位置的引用,从而导致 10 字节的内存泄漏。
因此,在对指针赋值前,一定确保内存位置不会失去控制。
2. 错误的内存释放
3. 返回值不正确的处理
有时候,某些函数会返回对动态分配的内存的引用,如下面的示例代码所示:
1. char *f() 2. { 3. return (char *)malloc(10); 4. } 5. void f1() 6. { 7. f(); 8. }
很明显,函数 f1 中对 f 函数的调用并未处理该内存位置的返回地址,其结果将导致 f 函数所分配的 10 个字节的块丢失,并导致内存泄漏。
4. 在内存分配后忘记使用free进行释放
最后,要避免这些内存相关的问题导致的内存越界与内存遗漏等错误,可以参考如下几点进行:
- 确保没有在访问空指针。
- 每个内存分配函数都应该有一个 free 函数与之对应,alloca 函数除外。
- 每次分配内存之后都应该及时进行初始化,可以结合 memset 函数进行初始化,calloc 函数除外。
- 每当向指针写入值时,都要确保对可用字节数和所写入的字节数进行交叉核对。
- 在对指针赋值前,一定要确保没有内存位置会变为孤立的。
- 每当释放结构化的元素(而该元素又包含指向动态分配的内存位置的指针)时,都应先遍历子内存位置并从那里开始释放,然后再遍历回父节点。
- 始终正确处理返回动态分配的内存引用的函数返回值。