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

2023-11-02

本文转自:http://blog.csdn.net/cjf_iceking/article/details/7552802
内存泄露是指程序在运行过程中动态申请的内存空间不再使用后没有及时释放,从而很可能导致应用程序内存无线增长。更广义的内存泄露包括未对系统的资源的及时释放,比如句柄等
内存溢出即用户在对其数据缓冲区操作时,超过了其缓冲区的边界;尤其是对缓冲区写操作时,缓冲区的溢出很可能导致程序的异常。

一.内存泄露

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

1.1先来看看内存泄露可能发生的一些场景:
(1)程序员常常忽略在所有的分支都加上内存的回收处理
int size = 100;  
char *pointer = new char[size];  
if (!xxxAPI(pointer, size)  
{  
    return;  
}  
delete[]pointer; 
2)构造函数中申请空间,析构函数中释放空间
(3)库函数或者系统API会在内部申请空间,然后返回指针给用户;以strdup为例
char *str;  
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函数

int _CrtSetDbgFlag(  
int newFlag  
);  

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

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

在main函数开始处添加:

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

则如果出现内存泄露Debug结束后,输出框将输出:
这里写图片描述
{150}表示申请的第150块申请的内存空间;
B) 显示内存泄露所在的文件以及行

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

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

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

这里写图片描述
C) 显示内存泄露处的堆栈

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

(函数详细信息参考:http://technet.microsoft.com/zh-cn/library/aa246759
此函数在指定的申请堆区空间次序处(即lBreakAlloc)设置断点;

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

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

//拷贝字符串,并且返回新的字符串地址  
char * string_copy(const char *source)  
{  
    char *p_string;  
    int string_len;  
    string_len = strlen(source);  
    if(source == NULL)  
    {  
        p_string = (char *)malloc(2*sizeof(char));  
        strcpy(p_string, "");  
    }  
    else  
    {   //这里错误 string_len+1  
    p_string = (char *)malloc((string_len)*sizeof(char));   
    strcpy(p_string, source);  
    }  
    return p_string;  
}  

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

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

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

最终确定了两个工具:

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

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

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

这里写图片描述

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • 为什么使用abs()或fabs()而不是条件否定?

    在 C C 中 为什么要使用abs or fabs 不使用以下代码即可查找变量的绝对值 int absoluteValue value lt 0 value value 这与较低级别的指令较少有关吗 您提出的 有条件的abs 并不等于std
  • 将处理后的图形绘制到另一个图形中

    我想将一个经过处理的图形绘制到另一个图形中 I have two graphics var gHead Graphics FromImage h var gBackground Graphics FromImage b Transform
  • Mono 无法保存用户设置

    我在 Mono Ubuntu 上保存用户设置时遇到问题 这是代码示例 private void Form1 Load object sender EventArgs e string savedText Properties Setting
  • 在 C++ 中分割大文件

    我正在尝试编写一个程序 该程序接受一个大文件 任何类型 并将其分成许多较小的 块 我想我已经有了基本的想法 但由于某种原因我无法创建超过 12 kb 的块大小 我知道谷歌等上有一些解决方案 但我更感兴趣的是了解这个限制的根源是什么 然后实际
  • 如何进行带有偏差的浮点舍入(始终向上或向下舍入)?

    我想以偏置舍入浮动 要么总是向下 要么总是向上 代码中有一个特定的点 我需要这个 程序的其余部分应该像往常一样四舍五入到最接近的值 例如 我想四舍五入到最接近的 1 10 倍数 最接近 7 10 的浮点数约为 0 69999998807 但
  • 捕获 foreach 条件中抛出的异常

    我有一个foreach在 foreach 本身的条件下循环期间中断的循环 有没有办法try catch抛出异常然后继续循环的项 这将运行几次 直到异常发生然后结束 try foreach b in bees exception is in
  • 有什么工具可以说明每种方法运行需要多长时间?

    我的程序的某些部分速度很慢 我想知道是否有我可以使用的工具 例如它可以告诉我可以运行 methodA 花了 100ms 等等 或者类似的有用信息 如果您使用的是 Visual Studio Team System 性能工具 中有一个内置分析
  • Linux TUN/TAP:无法从 TAP 设备读回数据

    问题是关于如何正确配置想要使用 Tun Tap 模块的 Linux 主机 My Goal 利用现有的路由软件 以下为APP1和APP2 但拦截并修改其发送和接收的所有消息 由Mediator完成 我的场景 Ubuntu 10 04 Mach
  • ASP .NET MVC,创建类似路由配置的永久链接

    我需要帮助在 MVC 网站中创建类似 URL 路由的永久链接 Slug 已设置为 www xyz com profile slug 代码为 routes MapRoute name Profile url profile slug defa
  • std::map 和二叉搜索树

    我读过 std map 是使用二叉搜索树数据结构实现的 BST 是一种顺序数据结构 类似于数组中的元素 它将元素存储在 BST 节点中并按其顺序维护元素 例如如果元素小于节点 则将其存储在节点的左侧 如果元素大于节点 则将其存储在节点的右侧
  • 是否有与 C++11 emplace/emplace_back 函数类似的 C# 函数?

    从 C 11 开始 可以写类似的东西 include
  • Xamarin Android:获取内存中的所有进程

    有没有办法读取所有进程 而不仅仅是正在运行的进程 如果我对 Android 的理解正确的话 一次只有一个进程在运行 其他所有进程都被冻结 后台进程被忽略 您可以使用以下代码片段获取当前正在运行的所有 Android 应用程序进程 Activ
  • 为什么 FTPWebRequest 或 WebRequest 通常不接受 /../ 路径?

    我正在尝试从 ftp Web 服务器自动执行一些上传 下载任务 当我通过客户端甚至通过 Firefox 连接到服务器时 为了访问我的目录 我必须指定如下路径 ftp ftpserver com AB00000 incoming files
  • 用于从字符串安全转换的辅助函数

    回到 VB6 我编写了一些函数 让我在编码时无需关心字符串的 null 和 数字的 null 和 0 等之间的区别 编码时 没有什么比添加特殊情况更能降低我的工作效率了用于处理可能导致一些不相关错误的数据的代码 9999 10000 如果我
  • UWP 无法在两个应用程序之间创建本地主机连接

    我正在尝试在两个 UWP 应用程序之间设置 TCP 连接 当服务器和客户端在同一个应用程序中运行时 它可以正常工作 但是 当我将服务器部分移动到一个应用程序并将客户端部分移动到另一个应用程序时 ConnectAsync 会引发异常 服务器未
  • C# 搜索目录中包含字符串的所有文件,然后返回该字符串

    使用用户在文本框中输入的内容 我想搜索目录中的哪个文件包含该文本 然后我想解析出信息 但我似乎找不到该字符串或至少返回信息 任何帮助将不胜感激 我当前的代码 private void btnSearchSerial Click object
  • 32位PPC rlwinm指令

    我在理解上有点困难rlwinmPPC 汇编指令 旋转左字立即然后与掩码 我正在尝试反转函数的这一部分 rlwinm r3 r3 0 28 28 我已经知道什么了r3 is r3在本例中是一个 4 字节整数 但我不确定这条指令到底是什么rlw
  • 内核开发和 C++ [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 从我know https stackoverflow com questions 580292 what languages are windo
  • 运行代码首先迁移更新数据库时出错

    我在迁移到数据库时遇到问题 并且似乎找不到我遇到的错误的答案 System MissingMethodException Method not found System Data Entity Migrations Builders Tab
  • 如何在 GCC 5 中处理双 ABI?

    我尝试了解如何克服 GCC 5 中引入的双重 ABI 的问题 但是 我没能做到 这是一个重现错误的非常简单的示例 我使用的GCC版本是5 2 如您所见 我的主要函数 在 main cpp 文件中 非常简单 main cpp include

随机推荐

  • QT获取布局器中控件的位置和大小

    在QT中 获取大小和位置有多种方式 有Size Geometry BaseSize pos Geometry rect 等等 但是这些函数都是获取控件没有布局时的位置和大小 如果控件被加入到布局管理器中 那么这些函数就获取不到位置和大小了
  • C语言指针详解(经典,非常详细)

    前言 复杂类型说明 要了解指针 多多少少会出现一些比较复杂的类型 所以我先介绍一下如何完全理解一个复杂类型 要理解复杂类型其实很简单 一个类型里会出现很多运算符 他们也像普通的表达式一样 有优先级 其优先级和运算优先级一样 所以我总结了一下
  • 华为机试:密码强度等级

    一 题目 密码按如下规则进行计分 并根据不同的得分为密码进行安全等级划分 一 密码长度 5 分 小于等于4 个字符 10 分 5 到7 字符 25 分 大于等于8 个字符 二 字母 0 分 没有字母 10 分 全都是小 大 写字 20 分
  • 判断字符串的两半是否相似

    给你一个偶数长度的字符串 s 将其拆分成长度相同的两半 前一半为 a 后一半为 b 两个字符串 相似 的前提是它们都含有相同数目的元音 a e i o u A E I O U 注意 s 可能同时含有大写和小写字母 如果 a 和 b 相似 返
  • 现代处理器性能分析及优化-序

    一直以来 工程师都有一个观念 性能为王 以前是 现在是 以后更是 根据 Data Never Sleeps 5 0 调查研究 世界上每天产生2 5万亿字节的数据 并且保持着每年25 的速度递增 在我们如今的社会生活中 信息产生的来源越来越多
  • pgbench和sysbench初次压测PG集群

    pgbench和sysbench初次压测PG集群 pgbench和sysbench是两个不同的压测工具 前者只适用于pg数据库 后者可以适用于mysql pg sql server等常见关系型数据库 若是关于tps的测试 建议采用sysbe
  • tomcat配置

    1 概念 参数名 描述 maxThreads 每一次HTTP请求到达Web服务 tomcat都会创建一个线程来处理该请求 那么最大线程数决定了Web服务容器可以同时处理多少个请求 maxThreads默认200 肯定建议增加 但是 增加线程
  • A Comprehensive Survey of Dataset Distillation

    本文是蒸馏学习综述系列的第三篇文章 A Comprehensive Survey of Dataset Distillation 的一个翻译 数据集蒸馏综述 摘要 1 引言 2 背景 2 1 形式化数据集蒸馏 3 元学习框架 3 1 通过时
  • 【自然语言处理】关系抽取 —— GDPNet 讲解

    GDPNet 论文信息 标题 GDPNet Refining Latent Multi View Graph for Relation Extraction 作者 Fuzhao Xue Aixin Sun Hao Zhang Eng Sio
  • Elasticsearch插件开发与调试

    背景 elasticsearch version 6 8 5 插件开发类型 elasticsearch 变更数据记录 插件开发 略 完善后开源 插件debug 远程debug 1 准备发行版的ES 下载 解压 2 将开发好的插件 zip包
  • Python基础知识(三):Python错误、警告、异常处理总结

    1 语法错误 Python 的语法错误或者称之为解析错 是初学者经常碰到的 如下实例 gt gt gt while True print Hello world File
  • STM32——理解中断与中断配置

    前言 本文将从 这是什么 为什么需要它 如何配置操作它 三个角度展开讨论分析 目录 中断简介 抢占优先级和子优先级 中断分组 配置要点 EXTI EXTI框图讲解 信号产生过程 编程要点 中断简介 中断 即机器运行过程中出现某些意外情况 需
  • XBOX怎么查保修期限

    网站 Microsoft 帐户 设备https account microsoft com devices fref home drawers devices manage devices refd account microsoft co
  • python实战爬取招聘网站职位数据

    大家都知道金三银四是每年的求职高峰期 是中国招聘市场中最热门的季节之一 这段时间内 许多公司会发布大量的招聘信息 吸引大批求职者前来应聘 同时 也有许多人选择这个时候跳槽 因为这个时候找到新工作的机会相对较大 python 疫情放开后感觉求
  • (websocket)协议中Ping Pong,Socket通讯ping pong(长连接),心跳包

    Socket读写数据 流Socket 数据包Socket 1 流套接字 SOCK STREAM 流套接字用于提供面向连接 可靠的数据传输服务 该服务将保证数据能够实现无差错 无重复发送 并按顺序接收 流套接字之所以能够实现可靠的数据服务 原
  • 华硕天选无法识别蓝牙

    系列文章目录 文章目录 系列文章目录 前言 一 解决 二 链接蓝牙 三 搜索蓝牙 前言 无法连接蓝牙 一 解决 电脑桌面右下角 点开 二 链接蓝牙 三 搜索蓝牙
  • sql中join与left-join图解区别

    t1表内容如下 t2表内容如下 下面来简述join和left join right join的区别 inner join select from t1 inner join t2 on t1 id t2 id 公共部分的数据才会被查询出来
  • matlab判断两个数据是否相等的相关问题

    在matlab中如何判断两个数据的值是否相等呢 我们都知道通常使用isequal 方法 调用方法如下 tf isequal A B 然而数据在什么情况下是相等的呢 我们看如下情况 x 1 38389652673674e 20 y 1 383
  • ubuntu 交叉编译qt 5.7 程序到 arm 开发板

    ubuntu 交叉编译qt 5 7 程序到 arm 开发板 平台 1 ubuntu 12 04 2 arm linux gcc 4 5 1 3 QT 5 7 4 开发板210 armcortex A8 一 概述 QT5的ARM移植及其中文显
  • 内存泄露、内存溢出以及解决方法

    本文转自 http blog csdn net cjf iceking article details 7552802 内存泄露是指程序在运行过程中动态申请的内存空间不再使用后没有及时释放 从而很可能导致应用程序内存无线增长 更广义的内存泄