内存泄露、内存溢出以及解决方法

内存泄露是指程序在运行过程中动态申请的内存空间不再使用后没有及时释放,从而很可能导致应用程序内存无线增长。更广义的内存泄露包括未对系统的资源的及时释放,比如句柄等。

内存溢出即用户在对其数据缓冲区操作时,超过了其缓冲区的边界;尤其是对缓冲区写操作时,缓冲区的溢出很可能导致程序的异常。

一.内存泄露

“知己知彼,方能百战不殆”,如果我们能够比较清楚的了解在编程的时候哪些情况容易导致内存泄露,通过避免这些糟糕的情况,从提高代码的质量本身出发,来抵御潜在导致内存泄露的发生。

1.1先来看看内存泄露可能发生的一些场景:

(1)程序员常常忽略在所有的分支都加上内存的回收处理

[cpp] view
plain
copy

  1. int size = 100;
  2. char *pointer = new char[size];
  3. if (!xxxAPI(pointer, size)
  4. {
  5. return;
  6. }
  7. delete[]pointer;

(2)构造函数中申请空间,析构函数中释放空间

(3)库函数或者系统API会在内部申请空间,然后返回指针给用户;以strdup为例

[cpp] view
plain
copy

  1. char *str;
  2. str = strdup("hello World!");

strdup申请了一段空间存储字符串"hello World",然后返回空间地址,这个时候用户经常会忘记释放str;

上面只是列出了简单的三种情况,尤其在一个复杂的大型系统中,一段内存的使用周期太长或者嵌套太深,还需要程序员自己去把握。

1.2.内存泄露的检测

(1)利用内存泄露检测工具

常用的有 BoundsCheaker、Deleaker、Visual Leak Detector等,工具毕竟熟能生巧,用户选择先自己喜欢的一款去用即可。

BoundsChecker没有找到win7下支持VS2005的破解版,用盗版的伤不起啊。

(2)使用Deleaker(本文采用vs2005)进行内存泄露检查

如下图所示:

A) Deleak安装后自动集成到VS中,在VS“工具”菜单中会加入一个“Deleaker”菜单项。

B) Deleaker能够对GDI,USER对象以及句柄进行检测,是否及时释放。

C) Deleaker能够检测泄露的内存发生地点,即展示其函数栈;双击能够转到相应的文件;

PS:Deleaker对中文不支持

如果有内存泄露Deleaker会在程序调试完弹出对话框如下图所示:

(3)使用Viual Leak detector

使用Deleak方便灵活,除了其对中文路径支持问题,但感觉和vs的集成度并不是很高。

Viual Leak detector安装后,要在VS中设置相应的头文件和库路径,在Debug模式下如果要检测相应源文件的内存泄露,则加上"#include <vld.h>"即可;

这样在检测内存泄露,可以在VS的输出窗口进行输出,感觉和VS的集成度更高,结果如下图所示:

同样能够显示 内存泄露处的 调用栈,并且通过双击也可以跳转到文件的内存泄露行,个人还是比较喜欢这种方式的。

(4)在没有工具的情况下,使用crtdbg.h中的api也是个很棒的选择

在MFC中可以看到在程序退出的时候,输出框内结尾部分输出内存泄露,并且点击可以跳转到内存泄露的代码处。

那么在console程序下呢,当然我们同样可以做到(做那些MFC帮我们完成了的细节);

A) _CrtSetDbgFlag函数

[cpp] view
plain
copy

  1. int _CrtSetDbgFlag(
  2. int newFlag
  3. );

(函数详细信息参考:http://msdn.microsoft.com/zh-cn/library/5at7yxcs.aspx

这个函数用于控制debug模式下堆管理的分配行为;

在main函数开始处添加:

[cpp] view
plain
copy

  1. _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
  2. //_CRTDBG_REPORT_FLAG:表示获取当前的标示位
  3. //_CRTDBG_LEAK_CHECK_DF:表示检测内存泄露

则如果出现内存泄露Debug结束后,输出框将输出:

{150}表示申请的第150块申请的内存空间;

B) 显示内存泄露所在的文件以及行

能够知道有内存泄露是不够的,更需要的信息是哪里内存泄露了?

我们可以在每个源文件的开头定义写这样一条宏定义:

[cpp] view
plain
copy

  1. //根据__FILE___和__LINE__能够确定文件和行
  2. #define new   new(_NORMAL_BLOCK, __FILE__, __LINE__)

C) 显示内存泄露处的堆栈

[cpp] view
plain
copy

  1. //lBreakAlloc,在申请的堆区序号为lBreakAlloc处设置一个断点
  2. long _CrtSetBreakAlloc( long lBreakAlloc );

(函数详细信息参考:http://technet.microsoft.com/zh-cn/library/aa246759

此函数在指定的申请堆区空间次序处(即lBreakAlloc)设置断点;

很喜欢这个函数,这个函数结合"A)"中提到的{150},比如使用方法:

[cpp] view
plain
copy

  1. _CrtSetBreakAlloc(150); //则在第150次申请堆空间时候设置断点

这样就可以看到函数调用栈,从而帮助我们更加精确的定位程序泄露的位置(调用栈可是个好玩意)。

个人感觉这种方式虽然要手动的修改代码,但其功能却比前两个工具的有效,因为能够在程序运行的时候查看调用栈,这就意味着能够调试程序。

展示结果如下图所示(自动在第150次申请堆空间处中断):

二.内存溢出

本篇最想分享的就是内存溢出的调试方法,内存溢出能够导致程序异常,而且这种异常使程序员难以下手。

2.1 内存溢出导致的异常症状

(1)内存异常经常产生的程序报错,如下图所示:

(2)有可能调试的时候不错,运行的时候出错,而且随机出现,这绝对让人很头疼的问题。

(3)庆幸的是,如果编译后的debug程序,直接运行后,如果出错,可以选择调试程序(如下图所示);

千万别以为麻烦就此可以解决了,进入调试状态后,发现出错的地方根本代码没有任何问题,可见内存溢出是个多么令人讨厌的家伙;

2.2 解决方法

虽然他是那么可恶,但也不要忘了是程序员自己一手创建了出来的。也不要灰心,困难总是有方法去解决的。

(1)等到生病的时候,再去看病,或许已经晚了;最好是提前做好预防准备;

A) 比如在程序中多使用strcpy_s、memcpy_s等具有缓冲区大小检查的函数,去取代strcpy、memcpy等;

B)给工程设置编译选项/WX开启(“将警告视为错误”),严格要求自己,这样很可能避免了不少潜在的bug;

C)  对自己的代码做好单元测试

(2)如果出现了这种难以查找的错误,可以从程序源码着手,查看一些和内存操作相关的函数,比如strcpy、memcpy等。

本人曾经在项目中就遇到用一个项目组成员在使用,strcpy拷贝一个字符串到一个空间不够的内存,从而导致程序异常:

[cpp] view
plain
copy

  1. //拷贝字符串,并且返回新的字符串地址
  2. char * string_copy(const char *source)
  3. {
  4. char *p_string;
  5. int string_len;
  6. string_len = strlen(source);
  7. if(source == NULL)
  8. {
  9. p_string = (char *)malloc(2*sizeof(char));
  10. strcpy(p_string, "");
  11. }
  12. else
  13. {   //这里错误 string_len+1
  14. p_string = (char *)malloc((string_len)*sizeof(char));
  15. strcpy(p_string, source);
  16. }
  17. return p_string;
  18. }

静态地去检查代码方法比较慢,而且不适用于大工程。

(3)检查工具

幸运的是本人接触了一个代码量较大的工程,不幸的是发生了内存溢出问题,而导致程序异常。而且出现的症状,就是调试不错,运行出错,

而且随机出现,并且内存异常的代码处,代码没有任何问题。这个问题纠结了至少一个月,病极乱投医,但找了一些工具大多用于检查内存泄露的。

最终确定了两个工具:

A)BoudsChecker,除了能够检查内存泄露,也能检查内存溢出问题;可惜的是没有找到Win7 下支持VS2005的破解版本

B)AppVerifier,专门用来检测那些用普通方法检测不出的意想不到的bug(比如内存溢出、错误句柄使用等)。而且AppVerifier使用非常简单,

只需要绑定需要测试的的应用程序,并且勾选测试项后保存,使用VS2005进行调试即可。AppVier:

PS:文中所称的内存溢出,用英文专业术语叫做heap corruption

转载于:http://blog.csdn.net/cjf_iceking/article/details/7552802

时间: 2024-11-05 06:20:18

内存泄露、内存溢出以及解决方法的相关文章

Android使用Handler造成内存泄露的分析及解决方法

一.什么是内存泄露? Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收.也就是说,一个对象不被任何引用所指向,则该对象会在被GC发现的时候被回收:另外,如果一组对象中只包含互相的引用,而没有来自它们外部的引用(例如有两个对象A和B互相持有引用,但没有任何外部对象持有指向A或B的引用),这仍然属于不可到达,同样会被GC回收. Android中使用Handler造成内存泄露的原因 private Han

内存泄漏以及常见的解决方法

  之所以撰写这篇文章是由于前段时间花费了非常大的精力在已经成熟的代码上再去处理memory leak问题.写此的目的是希望我们应该养成良好的编码习惯,尽可能的避免这种问题,由于当你对着一大片的代码再去处理此类的问题,此时无疑添加了解决的成本和难度.准确的说属于补救措施了. 1. 什么是内存泄漏(memory leak)?  指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况.内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内

c/c++服务器程序内存泄露问题分析及解决

由 www.169it.com 搜集整理 对于一个c/c++程序员来说,内存泄漏是一个常见的也是令人头疼的问题.已经有许多技术被研究出来以应对这个问题,比如 Smart Pointer,Garbage Collection等.Smart Pointer技术比较成熟,STL中已经包含支持Smart Pointer的class,但是它的使用似乎并不广泛,而且它也不能解决所有的问题:Garbage Collection技术在Java中已经比较成熟,但是在c/c++领域的发展并不顺畅,虽然很早就有人思考

vs2008内存泄露检测得到完美解决

Visual Leak Detector is a free, robust, open-source memory leak detection system for Visual C++. It's pretty easy to use. After installing it, you just need to tell Visual C++ where to find the included header and library file. Then it can be used wi

【转】Java学习---内存泄露与溢出的区别

Java内存泄露与溢出的区别 Java内存泄漏就是没有及时清理内存垃圾,导致系统无法再给你提供内存资源(内存资源耗尽): 而Java内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出. 内存溢出类似数组越届,超出你能存储的数据的上限 内存泄漏,就是内存使用完毕后,不能释放回收重新使用 Java内存泄露与溢出的区别 内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出. Java内存泄漏就是没有及时清理内存垃圾,导致系统无法再给你提供内存资源(

基于Java内存溢出的解决方法详解

一.内存溢出类型 1.java.lang.OutOfMemoryError: PermGen space JVM管理两种类型的内存,堆和非堆.堆是给开发人员用的上面说的就是,是在JVM启动时创建:非堆是留给JVM自己用的,用来存放类的信息的.它和堆不同,运行期内GC不会释放空间.如果web app用了大量的第三方jar或者应用有太多的class文件而恰好MaxPermSize设置较小,超出了也会导致这块内存的占用过多造成溢出,或者tomcat热部署时侯不会清理前面加载的环境,只会将context

Android EditText输入字数限制总结(包括中文输入内存溢出的解决方法)

限定EditText输入个数的解决方案很多,但是一般主要考虑两点,也就是处理两件事: (1)不同语言字符(英文.中文等)处理方式 (2)输入字符达到数目后,是否仍然允许用户输入 第一点,涉及的东东其实蛮多,不同语言在不同编码中占据字节数等,不同语言在U8等编码的表示范围等,这一整块知识很丰富, 自己暂时没有理的特别顺,稍后整理再说吧. 第二点,目前主流app的处理方案也各有不同,qq5.0以前的版本,发表说说貌似是没有字数限制的(我试了一个350字左右的照样发), 5.0以后限制了,这样如果用户

Android EditText输入字数限制总结(包含中文输入内存溢出的解决方法)

转载请注明,大飞:http://blog.csdn.net/rflyee/article/details/38856539 限定EditText输入个数的解决方式非常多,可是一般主要考虑两点.也就是处理两件事: (1)不同语言字符(英文.中文等)处理方式 (2)输入字符达到数目后,是否仍然同意用户输入 第一点,涉及的东东事实上蛮多,不同语言在不同编码中占领字节数等,不同语言在U8等编码的表示范围等,这一整块知识非常丰富,自己临时没有理的特别顺.稍后整理再说吧. 第二点.眼下主流app的处理方案也

Ecshop 后台导出订单Excel时, 内存溢出的解决方法

今天继续跟大家分享一下,在我配置Ecshop时的问题. 今天的问题是在后台想要导出订单列表Excel时出现的内存溢出.错误提示如下 问题:  Fatal error: Allowed memory size of 67108864 bytes exhausted (tried to allocate XXXXX) 原因: 出现此问题的原因是因为ECshop 项目中限定了 PHP 可以分配的内存大小.限制内存的作用是为了防止其他恶性插件滥用内存. 在我的项目中内存被限制在64MB,所以问题提示时出