C++内存泄漏和内存碎片的产生及避免策略

2023-11-02

1.内存泄漏的定义

 

   一般我们常说的内存泄漏是指堆内存的泄漏。堆内存是指程序从堆中分配的,大小任意的(内存块的大小可以在程序运行期决定),使用完后必须显示释放的内存。应用程序一般使用mallocreallocnew等函数从堆中分配到一块内存,使用完后,程序必须负责相应的调用freedelete释放该 内存块,否则,这块内存就不能被再次使用,我们就说这块内存泄漏了。

 

2、内存泄漏的后果

 

程序运行后置之不理,并且随着时间的流失消耗越来越多的内存(比如服务器上的后台任务,尤其是嵌入式系统中的后台任务,这些任务可能被运行后很多年内都置之不理);

新的内存被频繁地分配,比如当显示电脑游戏或动画视频画面时;

程序能够请求未被释放的内存(比如共享内存),甚至是在程序终止的时候;

泄漏在操作系统内部发生;

泄漏在系统关键驱动中发生;

内存非常有限,比如在嵌入式系统或便携设备中;

当运行于一个终止时内存并不自动释放的操作系统(比如AmigaOS)之上,而且一旦丢失只能通过重启来恢复。

 

3、如何发现内存泄漏

 

有些简单的内存泄漏问题可以从在代码的检查阶段确定。还有些泄漏比较严重的,即在很短的时间内导致程序或系统崩溃,或者系统报告没有足够内存,也比较容易发现。最困难的就是泄漏比较缓慢,需要观测几天、几周甚至几个月才能看到明显异常现象。那么如何在比较短的时间内检测出有没有潜在的内存泄漏问题呢?实际上不同的系统都带有内存监视工具,我们可以从监视工具收集一段时间内的堆栈内存信息,观测增长趋势,来确定是否有内存泄漏。在 Linux 平台可以用 ps 命令,来监视内存的使用,比如下面的命令 (观测指定进程的VSZ)

ps -aux

静态分析,包括手动检测和静态工具,是代价最小的方法。

1/当使用 C/C++ 进行开发时,采用良好的一致的编程规范是防止内存问题第一道也是最重要的措施。检测是编码标准的补充。二者各有裨益,但结合使用效果特别好。专业的 或 C++ 专业人员甚至可以浏览不熟悉的源代码,并以极低的成本检测内存问题。通过少量的实践和适当的文本搜索,您能够快速验证平衡的 *alloc() 和 free() 或者 new 和 delete 的源主体。人工查看此类内容通常会出现像清单 中一样的问题,可以定位出在函数 LeakTest 中的堆变量 Logmsg 没有释放。

 

2/代码静态扫描和分析的工具比较多,比如 splint, PC-LINT, BEAM 等。因为 BEAM 支持的平台比较多,这以 BEAM 为例,做个简单介绍,其它有类似的处理过程。

BEAM 可以检测四类问题没有初始化的变量;废弃的空指针;内存泄漏;冗余计算。而且支持的平台比较多。

BEAM 支持以下平台:

Linux x86 (glibc 2.2.4)

Linux s390/s390x (glibc 2.3.3 or higher)

Linux (PowerPC, USS) (glibc 2.3.2 or higher)

AIX (4.3.2+)

Window2000 以上

 

或者使用内嵌程序自动检测

可以重载内存分配和释放函数 new 和 delete,然后编写程序定期统计内存的分配和释放,从中找出可能的内存泄漏。或者调用系统函数定期监视程序堆的大小,关键要确定堆的增长是泄漏而不是合理的内存使用。这类方法比较复杂,在这就不给出详细例子了。

 

2、动态运行检测

实时检测工具主要有 valgrind, Rational purify 等。

1、 Valgrind

valgrind 是帮助程序员寻找程序里的 bug 和改进程序性能的工具。程序通过 valgrind 运行时,valgrind 收集各种有用的信息,通过这些信息可以找到程序中潜在的 bug 和性能瓶颈。

Valgrind 现在提供多个工具,其中最重要的是 MemcheckCachegrindMassif 和 CallgrindValgrind 是在 Linux 系统下开发应用程序时用于调试内存问题的工具。它尤其擅长发现内存管理的问题,它可以检查程序运行时的内存泄漏问题。其中的 memecheck 工具可以用来寻找 cc++ 程序中内存管理的错误。可以检查出下列几种内存操作上的错误:

读写已经释放的内存

读写内存块越界(从前或者从后)

使用还未初始化的变量

将无意义的参数传递给系统调用

内存泄漏

 2Rational purify

Rational Purify 主要针对软件开发过程中难于发现的内存错误、运行时错误。在软件开发过程中自动地发现错误,准确地定位错误,提供完备的错误信息,从而减少了调试时间。同时也是市场上唯一支持多种平台的类似工具,并且可以和很多主流开发工具集成。Purify 可以检查应用的每一个模块,甚至可以查出复杂的多线程或进程应用中的错误。另外不仅可以检查 C/C++,还可以对 Java 或 .NET 中的内存泄漏问题给出报告。

在 Linux 系统中,使用 Purify 需要重新编译程序。通常的做法是修改 Makefile 中的编译器变量。下面是用来编译本文中程序的 Makefile

CC=purify gcc

首先运行 Purify 安装目录下的 purifyplus_setup.sh 来设置环境变量,然后运行 make 重新编译程序。

./purifyplus_setup.sh

下面给出编译一个代码文件的示例,源代码文件命名为 test3.cpp. 用 purify 和 g++ 的编译命令如下,‘-g’是编译时加上调试信息。

purify g++ -g test3.cpp o test

运行编译生成的可执行文件 test,就可以得到图1,可以定位出内存泄漏的具体位置。

./test

 

4、如何避免内存泄漏

其实内存泄漏的原因可以概括为:调用了malloc/new等内存申请的操作,但缺少了对应的free/delete,总之就是,malloc/newfree/delete的数量多。我们在编程时需要注意这点,保证每个malloc都有对应的free,每个new都有对应的deleted!!!平时要养成这样一个好的习惯。

 

要避免内存泄漏可以总结为以下几点:

 

1、程序员要养成良好习惯,保证malloc/newfree/delete匹配;

2、一遍又一遍的看代码,希望能看出内存泄露的bug 

3、检查malloc/newfree/delete是否匹配,一些工具也就是这个原理。要做到这点,就是利用宏或者钩子,在用户程序与运行库之间加了一层,用于记录内存分配情况。

4.、总结了好些规律:要成对使用,free掉的内存指针要置空,在尽量少的函数中申请和释放内存,等等 

5、使用了大量的内存村泄露检测工具,结合各种自动测试软件,采取疯狂加变态的测试方法,试图找出所有可能的内存泄露bug。 

  6、代码规模超过千万行,内存从一个模块被传递到另一个或多个,谁知道传递给哪个模块,最后在那里释放的了,反正我保证那个指针指向的数据是好的就行了。由于代码的规模,穷举式的测试根本不可能,只有一遍一遍的看代码,祈祷自己的代码没有内存泄露的问题。 

 

 

内存碎片

 

1、什么是内存碎片

   

    内存碎片---描述一个系统中所有的不可用的空闲内存;这些资源之所以仍然未被使用,是因为负责分配内存的分配器使这些内存无法使用。这一问题通常都会发生,原因在于空闲内存以小而不连续方式出现在不同的位置。由于分配方法决定内存碎片是否是一个问题,因此内存分配器在保证空闲资源可用性方面扮演着重要的角色。

 

2、内存碎片产生的原因

 

    原因在与空闲内存以小而不连续的方式出现在不同的位置(内存分配较小,并且分配的这些小的内存生存周期又较长,反复申请后将产生内存碎片的出现)。内存分配程序浪费内存的基本方式有三种:即额外开销、内部碎片以及外部碎片(图 1)。内存分配程序需要存储一些描述其分配状态的数据。这些存储的信息包括任何一个空闲内存块的位置、大小和所有权,以及其它内部状态详情。一般来说,一个运行时间分配程序存放这些额外信息最好的地方是它管理的内存。内存分配程序需要遵循一些基本的内存分配规则。例如,所有的内存分配必须起始于可被 4或 16 整除(视处理器体系结构而定)的地址。内存分配程序把仅仅预定大小的内存块分配给客户,可能还有其它原因。当某个客户请求一个 43 字节的内存块时,它可能会获得 44字节、48字节 甚至更多的字节。由所需大小四舍五入而产生的多余空间就叫内部碎片。

  外部碎片的产生是当已分配内存块之间出现未被使用的差额时,就会产生外部碎片。例如,一个应用程序分配三个连续的内存块,然后使中间的一个内存块空闲。内存分配程序可以重新使用中间内存块供将来进行分配,但不太可能分配的块正好与全部空闲内存一样大。倘若在运行期间,内存分配程序不改变其实现法与四舍五入策略,则额外开销和内部碎片在整个系统寿命期间保持不变。虽然额外开销和内部碎片会浪费内存,因此是不可取的,但外部碎片才是嵌入系统开发人员真正的敌人,造成系统失效的正是分配问题。

 

3、内存碎片的弊端与优点

 

    缺点:

 

    大量的内存碎片会使系统缓慢,原因在于虚拟内存的使用会使内存与硬盘之间的数据交换称为系统

 

缓慢的根源,最终造成内存的枯竭!

 

   优点:

 

   减少内存碎片,提高分配速度,便于内存管理,防止内存泄露

 

4、如何避免内存碎片的产生

  

   1,少用动态内存分配的函数(尽量使用栈空间)

 

   2,分配内存和释放的内存尽量在同一个函数中

 

   3,尽量一次性申请较大的内存2的指数次幂大小的内存空间,而不要反复申请小内存(少进行内存的分割)

 

   4,使用内存池来减少使用堆内存引起的内存碎片

   5、尽可能少地申请空间。

   6尽量少使用堆上的内存空间~

   7做内存池,也就是自己一次申请一块足够大的空间,然后自己来管理,用于大量频繁地new/delete操作。

 

   内存管理系统将能够急时合并相邻空闲内存块,得到更大的空闲内存。这样并不会导致内存碎片的出现。即使相邻空间不空闲,这样产生的碎片还是比较少的,但是对于游戏(运行时间较长)或者手机(内存较小)

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

C++内存泄漏和内存碎片的产生及避免策略 的相关文章

随机推荐

  • OpenMMLab MMYOLO目标检测环境搭建(一)

    1 环境搭建 conda create n mmyolo python 3 7 y 创建环境 conda activate mmyolo 激活环境 conda install pytorch torchvision torchaudio c
  • 【获奖公布】征文

    AI Artificial Intelligence 即人工智能 人工智能领域的研究包括机器人 语音识别 图像识别 自然语言处理和专家系统等 人工智能从诞生以来 理论和技术日益成熟 应用领域也不断扩大 可以设想 未来人工智能带来的科技产品
  • 董事长、CEO、总裁、总经理的关系

    董事长 公司董事会主席 直接领导公司里的董事会 以及附设的执行委员会 任免委员会 薪酬委员会 审计委员会等一些专门委员会 是公司的老大 首席执行官 CEO 公司董事会的代理人 执行董事会授予的部分经营管理权利 是公司政策执行机构的最高负责人
  • 自己的小碎念

    现在是2019年6月26日下午15 11 我打算写一篇日记 记下这一天的经过 早晨七点 山鸡疯狂敲门 吓得我擦点猝死 我们宿舍都起床准备参加毕业典礼 像往常一样洗漱 聊天 然后去了礼堂门口 我们在那里拿着发的歌词 也没多看几眼 和朋友们闲聊
  • rocketmq客户端日志配置

    rocketmq客户端会打印一些日志如消费进度 心跳等 默认的是滚动10个日志文件 每个100MB 又一次看到这个日志目录有几十GB 手动删除 但是悲剧的是rocketmq没有释放这个日志目录 导致不能真正的释放磁盘空间 于是研究了下怎么配
  • Android Studio 的 Android Monitor / DDMS 中不显示应用的进程(看不到 log)

    http blog csdn net u011240877 article details 53160119 问题 不知道怎么 app 对应的进程在 Android Monitor DDMS 中都看不到了 想调试或者看个日志都不行 分析 一
  • 拳王虚拟项目公社:闲鱼最强的实操赚钱方法,你一定不知道!

    闲鱼 很多人都很喜欢的一个二手交易平台 也很多人喜欢在这个平台上赚钱 于是乎就不断的找各种方法 教程 在拳王公ZH的后台每天都会有朋友搜索免费的闲鱼资源 所以拳王不得不为这些朋友们着想一下 拳王今天就满足大众的需求 写一篇自己总结实操过的干
  • python统计单词个数

    统计句子单词个数 mydict for i in input 英文句子 split if i in mydict mydict i 1 else mydict i 1 for key value in mydict items print
  • 推荐 7 月份 yyds 的开源项目

    本期推荐开源项目目录 1 Notion 的开源替代方案 2 Flutter 富文本编辑器 3 所有 Demo 的妈 4 30秒的代码 5 面向程序员的全栈资源集 6 基于项目的学习 7 开源管理系统 01 Notion 的开源替代方案 Ap
  • axios token失效刷新token怎么重新请求_Token 刷新并发处理解决方案

    对 Token 进行刷新续期 我们要解决并发请求导致重复刷新 Token 的问题 这也是设计刷新 Token 的难点 这里我会分别介绍前端和后端各自的处理方案 后端方案 利用 Redis 缓存 当同时发起多个请求时 第一个接口刷新了 Tok
  • anaconda spyder使用技巧

    spyder 很简单的python代码编辑器 目录 界面布局 快捷键 设置语言 设置默认打开文件夹 高级技巧 调试代码 高级技巧 创建spyder虚拟环境 恢复默认布局 英语阅读难的话 可以先跳到设置语言 界面布局 选项卡 file 文件
  • k8s生产环境容器内部JVM参数配置解析及优化

    试问 我们为什么要做jvm参数配置优化 避免默认条件或者复杂情况导致频繁STW或者OOM 1 堆总内存初始化大小分配和最大值分配 Xms512m Xmx512m 设置为FullGC之后的老年代内存占用的3 4倍 线下调试 1 初始分配的堆内
  • 华为OD机试真题-服务中心的最佳位置【2023Q2】【JAVA、Python、C++】

    题目描述 一家快递公司希望在一条街道建立新的服务中心 公司统计了该街道中所有区域在地图上的位置 并希望能够以此为依据为新的服务中心选址 使服务中心 到所有区域的距离的总和最小 给你一个数组 positions 其中 positions i
  • 【#ifndef, #define, 和 #endif】

    前言 学习AFNetWoring源码的时候 在AFN的h借接口文件又看到了这几个宏定义 学习记录一下 作用 ifndef define 和 endif是C CPP的预处理指令 常常用来条件编译和防止头文件重复包含 简介 ifndef 它是i
  • rpm安装postgresql12

    安装环境 CentOS Linux release 7 3 1611 Core 安装pg版本 postgresql12 安装步骤 文件下载地址 postgresql12 docker19 postgis Linux文档类资源 CSDN下载
  • 2021-09-09

    将100 200之间的素数输出 谭浩强 C程序设计 第四版 第二章习题第四题的六小题代码实现 将100 200之间的素输出 include
  • CSDN获取积分办法

    下载积分攻略 1 个人设置里进行手机绑定CSDN账户 奖励50分 右上角设置 账户安全 手机绑定 2 完成任务送若干分积分 CSDN有奖任务 3 上传有效资源获取积分 上传非法 广告资源用户 将被扣除一定积分 严重者封号 上传自己设分资源被
  • 华为云云耀云服务器L实例评测| ultralytics最先进模型YOLOv8深度学习AI训练

    目录 前言 登录服务器 安装pyhton 部署yolov8 安装Pytorch 下载权重文件 训练模型 模型使用 前言 前几期我们在云耀云服务器L实例上分别使用docker和直接在centos上部署了yolov5识别API 前端项目vue
  • 方法的定义和格式

    方法 什么是方法 方法是程序中最小的执行单元 定义 把一些代码打包在一起 该过程称为方法 实际开发过程中 什么时候用到方法 重复的代码 具有独立功能的代码可以抽取到方法中 实际开发中 方法的好处 提高代码的复用性 提高代码的可维护性 方法的
  • C++内存泄漏和内存碎片的产生及避免策略

    1 内存泄漏的定义 一般我们常说的内存泄漏是指堆内存的泄漏 堆内存是指程序从堆中分配的 大小任意的 内存块的大小可以在程序运行期决定 使用完后必须显示释放的内存 应用程序一般使用malloc realloc new等函数从堆中分配到一块内存