内存泄漏、内存溢出和解决方案

执行过程中的内存空间未释放时应用程序后,动态内存泄漏不再使用,因此,很可能导致应用程序的内存无线增长。泄露包含未对系统的资源的及时释放,比方句柄等。

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

一.内存泄露

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

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)给project设置编译选项/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. }

静态地去检查代码方法比較慢,并且不适用于大project。

(3)检查工具

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

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

终于确定了两个工具:

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

B)AppVerifier。专门用来检測那些用普通方法检測不出的意想不到的bug(比方内存溢出、错误句柄使用等)。

并且AppVerifier使用很easy,

仅仅须要绑定须要測试的的应用程序,而且勾选測试项后保存,使用VS2005进行调试就可以。AppVier:

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

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

版权声明:本文博客原创文章,博客,未经同意,不得转载。

时间: 2024-11-05 14:38:46

内存泄漏、内存溢出和解决方案的相关文章

关于内存溢出和内存泄漏

内存泄漏 内存泄漏(memory leak): 是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光. 一般我们所说的内存泄漏是指堆内存的泄漏,堆内存是指程序从堆中分配的,大小任意的(内存块的大小可以在程序运行期决定),使用完成之后必须显示释放内存.应用程序一般使用malloc.realoc.new等函数从堆中分配到一块内存块,使用完成后,程序必须负责相应的释放.在C中使用free(),C++中delete.delete[

java中内存溢出和内存泄漏的区别

虽然在java中我们不用关心内存的释放, 垃圾回收机制帮助我们回收不需要的对象,但实际上不正当的操作也会产生内存问题:如,内存溢出.内存泄漏 内存溢出:out of memory:简单通俗理解就是内存不够用了 . 内存泄漏:leak of memory:一个对象分配内存之后,在使用结束时未及时释放,导致一直占用内存,没有及时清理,使实际可用内存减少,就好像内存泄漏了一样. 比如在jdk6中不恰当使用substring()方法容易引发内存泄漏,JDK7 就无需考虑 jdk6的substring源码

C++内存溢出和内存泄漏?

1.内存溢出 内存溢出是指程序在申请内存时没有足够的内存空间供其使用.原因可能如下: (1)内存中加载的数据过于庞大: (2)代码中存在死循环: (3)递归调用太深,导致堆栈溢出等: (4)内存泄漏最终导致内存溢出: 2.内存泄漏 内存泄漏是指使用new申请内存, 但是使用完后没有使用delete释放内存,导致占用了有效内存. 原文地址:https://www.cnblogs.com/dingou/p/10549435.html

内存泄漏/溢出

内存泄漏 (Memory Leak):是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄漏似乎不会有大的影响,但内存泄漏堆积后的后果就是内存溢出. 内存溢出 (Memory Overflow):指程序申请内存时,没有足够的内存供申请者使用,或者说,给了你一块存储int类型数据的存储空间,但是你却存储long类型的数据,那么结果就是内存不够用,此时就会报错OOM,即所谓的内存溢出. 关联 内存泄漏的堆积最终会导致内存溢出. 内存泄漏 --> 内存溢出 参考: https://www.zhi

java的GC与内存泄漏

从诞生至今,20多年过去,Java至今仍是使用最为广泛的语言.这仰赖于Java提供的各种技术和特性,让开发人员能优雅的编写高效的程序.今天我们就来说说Java的一项基本但非常重要的技术内存管理 了解C语言的同学都知道,在C语言中内存的开辟和释放都是由我们自己来管理的,每一个new操作都要对于一个delete操作,否则就会参数内存泄漏和溢出的问题,导致非常槽糕的后果.但在Java开发过程中,则完全不需要担心这个问题.因为jvm提供了自动内存管理的机制.内存管理的工作由jvm帮我们完成.这样我们就不

TextVersion_02_内存泄漏

内存泄漏 Android 的虚拟机是基于寄存器的 Dalvik,它的最大堆大小一般是 16M,有的机器为 24M.因此我们所能利用 的内存空间是有限的.如果我们的内存占用超过了一定的水平就会出现 OutOfMemory 的错误. 什么情况会导致内存泄漏 1.资源释放问题 长期保持某些资源,比如Context,Cursor,IO流的引用,资源得不到释放造成内存泄漏 2.对象内存过大问题 保存了多个耗用内存过大的对象(如 Bitmap.XML 文件),造成内存超出限制. 3.static 关键字的使

Android实战——LeakCanary检测内存泄漏

本篇文章包括以下内容: 前言 内存泄漏的简介 内存溢出的简介 LeakCanary的配置与使用 结语 内存泄漏对于初学者们可能是一个陌生的词语,但是却频频发生于自己的软件上,只不过自己不知道而已.同理,内存溢出也是一个道理.而内存泄漏和内存溢出常常是面试的考题,所以早点掌握是必不可少的 内存泄漏是指:对象在它有限的生命周期结束时,它们将被垃圾回收,如果在回收时,这个对象还被一系列的引用,导致该对象不会被回收,那么就会导致内存泄漏.随着泄漏的累积,应用将消耗完内存,应用的流畅性就会大大减弱 常见的

java内存分配与溢出

Java程序而言,Java虚拟机有自动内存管理机制,不需要开发人员去手动释放内空间,也不容易出现内存泄漏和溢出的问题,一切看起来都很完美.一旦出现内存泄漏和溢出方面的问题,如果不了解Java虚拟机是怎么样使用内存的,那么排查起来将困难.以往对内存的理解仅仅停留在栈.堆这两个部分,其实Java虚拟机的还有其他分区远比这复杂.接下来将介绍Java虚拟机主要的几个区域及其作用.内存溢出. java虚拟机在执行Java程序时会把其管理的内存划分为若干个不同的数据区域,这些区域都有各自的用途.创建和销毁时

利用 LeakCanary 来检查 Android 内存泄漏

前言 你被概率性的 OOM 困扰么?有时候,OOM 像幽灵一样,挥之不去,可真想把它揪出来时,又捉之不着.或许,是时候用 LeakCanary 来诊断一下了.它是一个用来检查 Android 下内存泄漏的开源库,这篇文章主要介绍其用法.架构和其背后的实现原理. Square 有篇文章介绍了开发这个库的原因.他们的一个付款流程里,需要用到用户的签名,他们直接用 Bitmap 来画签名,Bitmap 大小和屏幕分辨率是一样的.问题来了,在试图创建这个 Bitmap 对象时,概率性 OOM 如幽灵般相

C 语言中的指针和内存泄漏

引言 对于任何使用 C 语言的人,如果问他们 C 语言的最大烦恼是什么,其中许多人可能会回答说是指针和内存泄漏.这些的确是消耗了开发人员大多数调试时间的事项.指针和内存泄漏对某些开发人员来说似乎令人畏惧,但是一旦您了解了指针及其关联内存操作的基础,它们就是您在 C 语言中拥有的最强大工具. 本文将与您分享开发人员在开始使用指针来编程前应该知道的秘密.本文内容包括: 导致内存破坏的指针操作类型 在使用动态内存分配时必须考虑的检查点 导致内存泄漏的场景 如果您预先知道什么地方可能出错,那么您就能够小