内存泄漏与内存溢出

来源:互联网 发布:数据库采集 编辑:程序博客网 时间:2024/05/22 05:23

内存泄漏形象的比喻是“操作系统可提供给所有进程的存储空间正在被某个进程榨干”,最终结果是程序运行时间越长,占用存储空间越来越多,最终用尽全部存储空间,整个系统崩溃。所以“内存泄漏”是从操作系统的角度来看的。这里的存储空间并不是指物理内存,而是指虚拟内存大小,这个虚拟内存大小取决于磁盘交换区设定的大小。由程序申请的一块内存,如果没有任何一个指针指向它,那么这块内存就泄露了。

内存泄露是指程序中间动态分配了内存,但是在程序结束时没有释放这部分内存,从而造成那一部分内存不可用的情况,重起计算机可以解决,但是也有可能再次发生内存泄露,内存泄露和硬件没有关系,它是由软件引起的。

以发生的方式来分类,内存泄漏可以分为4类:

常发性
发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。

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

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

隐式
程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天、几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。

从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,作为一般的用户,根本感觉不到内存泄漏的存在。真正有危害的是内存泄漏的堆积,这会最终消耗尽系统所有的内存。从这个角度来说,一次性内存泄漏并没有什么危害,因为它不会堆积,而隐式内存泄漏危害性则非常大,因为较之于常发性和偶发性内存泄漏它更难被检测到。

其实内存泄漏的原因可以概括为:调用了malloc/new等内存申请的操作,但缺少了对应的free/delete,总之就是,malloc/new比free/delete的数量多。我们在编程时需要注意这点,保证每个malloc都有对应的free,每个new都有对应的deleted!!!平时要养成这样一个好的习惯。
 
要避免内存泄漏可以总结为以下几点:
 •程序员要养成良好习惯,保证malloc/new和free/delete匹配;
 •检测内存泄漏的关键原理就是,检查malloc/new和free/delete是否匹配,一些工具也就是这个原理。要做到这点,就是利用宏或者钩子,在用户程序与运行库之间加了一层,用于记录内存分配情况。


由内存泄露引出内存溢出话题:

所谓内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是会产生内存溢出的问题。
常见的溢出主要有:

内存分配未成功,却使用了它。
常用解决办法是,在使用内存之前检查指针是否为NULL。如果指针p 是函数的参数,那么在函数的入口处用assert(p!=NULL)进行检查。如果是用malloc 或new 来申请内存,应该用if(p==NULL)或if(p!=NULL)进行防错处理。

内存分配虽然成功,但是尚未初始化就引用它。
内存分配成功并且已经初始化,但操作越过了内存的边界。
例如在使用数组时经常发生下标“多1”或者“少1”的操作。特别是在for 循环语句中,循环次数很容易搞错,导致数组操作越界。

使用free 或delete 释放了内存后,没有将指针设置为NULL产生“野指针”。


程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内存,此时应该重新设计数据结构,从根本上解决对象管理的混乱局面。
不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。

内存泄漏实例

例1:
#include<stdio.h>
#include<malloc.h>
#define MAX 100000000
int main(void){


int *a[MAX] = {NULL};
int i;

 for(i=0;i<MAX;i++){
  a[i] = (int*)malloc(MAX);
 }

return0;
}

例2:

#include "stdio.h"
#include "malloc.h"//malloc()函数被包含在malloc.h里面
int main(void){

char*a=NULL;//声明一个指向a的char*类型的指针
a=(char*)malloc(100*sizeof(char));//使用malloc分配内存的首地址,然后赋值给a

if(!a){
 perror("malloc");//如果malloc失败,可以得到一些log
 return-1;
}
sprintf(a,"%s","HelloWorld\n");//"HelloWorld\n"写入a指向的地址

printf("%s\n",a);//输出用户输入的数据
free(a);//释放掉使用的内存地址

return0;
}

例1:对malloc申请之后没有检测返回值;
例2:检测malloc返回值条件有误。

 void   fun0()  
  {  
      char   *p=new   char[100];  
  }  
  执行完上面的函数就发生了泄露  
  指针p是局部变量,函数执行完后,指针p被销毁,造成   new   char[100]的内存没有指针指向它  
  ,也就无法再使用,造成内存泄漏。 
  void   fun1()  
  {  
      char   *p=new   char[100];  
      p=new   char[100];  
  }  
  这也泄露了  
  
  void   fun2()  
  {  
      new   char[100];  
  }  
  这东西肯定泄露

0 0