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


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

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

    一.内存泄露

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

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

    (1)程序猿经常忽略在全部的分支都加上内存的回收处理

    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为例

    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函数

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

    (函数具体信息參考:http://msdn.microsoft.com/zh-cn/library/5at7yxcs.aspx

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

    在main函数開始处加入:

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

    则假设出现内存泄露Debug结束后。输出框将输出:

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

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

    可以知道有内存泄露是不够的。更须要的信息是哪里内存泄露了?

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

    1. //依据__FILE___和__LINE__可以确定文件和行  
    2. #define new   new(_NORMAL_BLOCK, __FILE__, __LINE__)    


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

    1. //lBreakAlloc。在申请的堆区序号为lBreakAlloc处设置一个断点  
    2. long _CrtSetBreakAlloc( long lBreakAlloc );  
    (函数具体信息參考:http://technet.microsoft.com/zh-cn/library/aa246759

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

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

    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拷贝一个字符串到一个空间不够的内存,从而导致程序异常:

    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

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

  • 相关阅读:
    (转)怎样获得与激活Windows 7超级管理员权限
    (转) C代码优化方案
    英语词汇立体记忆 02
    (转)LUA和C之间的函数调用
    通过lua自带例子学习lua 01
    英语词汇立体记忆 01
    反射(类加载内存分析)
    反射(类加载器的作用)
    反射(动态创建对象操作属性)
    反射(分析类初始化)
  • 原文地址:https://www.cnblogs.com/hrhguanli/p/4632798.html
Copyright © 2020-2023  润新知