在计算机科学中,内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。内存泄漏与许多其他问题有着相似的症状,并且通常情况下只能由那些可以获得程序源代码的程序员才可以分析出来。然而,有不少人习惯于把任何不需要的内存使用的增加描述为内存泄漏,即使严格意义上来说这是不准确的。
一般我们常说的内存泄漏是指堆内存的泄漏。堆内存是指程序从堆中分配的,大小任意的(内存块的大小可以在程序运行期决定),使用完后必须显示释放的内存。应用程序一般使用malloc,realloc,new等函数从堆中分配到一块内存,使用完后,程序必须负责相应的调用free或delete释放该内存块,否则,这块内存就不能被再次使用,我们就说这块内存泄漏了。
内存泄漏会因为减少可用内存的数量从而降低计算机的性能。最终,在最糟糕的情况下,过多的可用内存被分配掉导致全部或部分设备停止正常工作,或者应用程序崩溃。
内存泄漏可能不严重,甚至能够被常规的手段检测出来。在现代操作系统中,一个应用程序使用的常规内存在程序终止时被释放。这表示一个短暂运行的应用程序中的内存泄漏不会导致严重后果。
在以下情况,内存泄漏导致较严重 后果:
* 程序运行后置之不理,并且随着时间的流失消耗越来越多的内存(比如服务器上的后台任务,尤其是嵌入式系统中的后台任务,这些任务可能被运行后很多年内都置之不理);
* 新的内存被频繁地分配,比如当显示电脑游戏或动画视频画面时;
* 程序能够请求未被释放的内存(比如共享内存),甚至是在程序终止的时候;
* 泄漏在操作系统内部发生;
* 泄漏在系统关键驱动中发生;
* 内存非常有限,比如在嵌入式系统或便携设备中;
* 当运行于一个终止时内存并不自动释放的操作系统(比如AmigaOS)之上,而且一旦丢失只能通过重启来恢复。
内存泄露分类:
(1). 常发性内存泄漏。发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。
(2). 偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。
(3). 一次性内存泄漏。发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。比如,在类的构造函数中分配内存,在析构函数中却没有释放该内存,所以内存泄漏只会发生一次。
(4). 隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。
从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,作为一般的用户,根本感觉不到内存泄漏的存在。真正有危害的是内存泄漏的堆积,这会最终消耗尽系统所有的内存。从这个角度来说,一次性内存泄漏并没有什么危害,因为它不会堆积,而隐式内存泄漏危害性则非常大,因为较之于常发性和偶发性内存泄漏它更难被检测到。
内存泄露例-1:
[C++] 纯文本查看 复制代码
char *memp1 = malloc(10);
char *memp2 = malloc(10);
memp1 = memp2;
像上面的代码,memp1 和 memp2 分别指向了为各自分配的具有 10 个字节大小的空间。但在第 3 句时,memp1 指向了 memp2 的空间,这样就会导致 memp1 原来指向的那个 10 字节的内存变成了孤立的内存块,从而发生了内存泄露。
内存泄露例-2:
释放父块后造成的内存泄露,示例代码如下:
[C++] 纯文本查看 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct memstrc {
int k;
int b;
char *p;
};
int main(void)
{
char *hp = "hello world";
struct memstrc *memp = (struct memstrc *)malloc (sizeof(struct memstrc));
memp->p = malloc(64);
memcpy (memp->p, hp, strlen(hp));
*(memp->p + strlen(hp)) = '\0';
printf ("%s\n", memp->p);
//free (memp->p);
free (memp);
return (0);
}
比如在上面的程序中,分配了两个内存指针,一个是结构体指针 memp ,另一个是结构体内部的成员字符指针 p 。当在最后我们只是直接释放 memp 所指向的内存块,那么就会造成 p 所指向的内存变为孤立的,所以造成了内存泄露。
防止办法是,每当释放结构化的元素时,而该元素又包含了指向动态分配的内存位置指针时,应首先遍历子内存位置,并释放它,最后再释放父节点。
所以,象上面正确的做法是取消掉 free (memp->p); 这句话的注释。 |