内存泄漏的原因,内存泄漏如何避免?内存泄漏如何定位?
1. 内存溢出
内存溢出 OOM (out of memory),是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个int,但给它存了long才能存下的数,那就是内存溢出。
2. 内存泄漏
内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。最终的结果就是导致OOM。
内存泄漏是指你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的那块内存你自己也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序。
3. 造成内存泄露常见的三种情况
1,指针重新赋值
2,错误的内存释放
3,返回值的不正确处理
3.1 指针重新赋值
如下代码:
char * p = (char *)malloc(10);
char * np = (char *)malloc(10);
其中,指针变量 p 和 np 分别被分配了 10 个字节的内存。
如果程序需要执行如下赋值语句:
p=np;
这时候,指针变量 p 被 np 指针重新赋值,其结果是 p 以前所指向的内存位置变成了孤立的内存。它无法释放,因为没有指向该位置的引用,从而导致 10 字节的内存泄漏。
因此,在对指针赋值前,一定确保内存位置不会变为孤立的。
类似的情况,连续重复new的情况也是类似:
int *p = new int; p = new int...;//错误
3.2 错误的内存释放
假设有一个指针变量 p,它指向一个 10 字节的内存位置。该内存位置的第三个字节又指向某个动态分配的 10 字节的内存位置。
如果程序需要执行如下赋值语句时:
free(p);
很显然,如果通过调用 free 来释放指针 p,则 np 指针也会因此而变得无效。np 以前所指向的内存位置也无法释放,因为已经没有指向该位置的指针。换句话说,np 所指向的内存位置变为孤立的,从而导致内存泄漏。
因此,每当释放结构化的元素,而该元素又包含指向动态分配的内存位置的指针时,应首先遍历子内存位置(如本示例中的 np),并从那里开始释放,然后再遍历回父节点,如下面的代码所示:
free(p->np);
free(p);
3.3 返回值的不正确处理
有时候,某些函数会返回对动态分配的内存的引用,如下面的示例代码所示:
char *f(){return (char *)malloc(10);
}
void f1(){f();
}
函数 f1 中对 f 函数的调用并未处理该内存位置的返回地址,其结果将导致 f 函数所分配的 10 个字节的块丢失,并导致内存泄漏。
4 在内存分配后忘记使用 free 进行释放
4. 如何避免内存泄露?
-
确保没有在访问空指针。
-
每个内存分配函数都应该有一个 free 函数与之对应,alloca 函数除外。
-
每次分配内存之后都应该及时进行初始化,可以结合 memset 函数进行初始化,calloc 函数除外。
-
每当向指针写入值时,都要确保对可用字节数和所写入的字节数进行交叉核对。
-
在对指针赋值前,一定要确保没有内存位置会变为孤立的。
-
每当释放结构化的元素(而该元素又包含指向动态分配的内存位置的指针)时,都应先遍历子内存位置并从那里开始释放,然后再遍历回父节点。
-
始终正确处理返回动态分配的内存引用的函数返回值。
5.定位内存泄漏(valgrind)(重点)
5.1、基本概念
Valgrind是一个GPL的软件,用于Linux(For x86, amd64 and ppc32)程序的内存调试和代码剖析。你可以在它的环境中运行你的程序来监视内存的使用情况,比如C 语言中的malloc和free或者 C++中的new和 delete。使用Valgrind的工具包,你可以自动的检测许多内存管理和线程的bug,避免花费太多的时间在bug寻找上,使得你的程序更加稳固。
安装Valgrind
//valgrind下载:
http://valgrind.org/downloads/valgrind-3.12.0.tar.bz2valgrind安装:
1. tar -jxvf valgrind-3.12.0.tar.bz2
2. cd valgrind-3.12.0
3. ./configure
4. make
5. sudo make install
应用环境:Linux
编程语言:C/C++
使用方法: 编译时加上-g选项,如 gcc -g filename.c -o filename,使用如下命令检测内存使用情况:
最常用的命令格式:
valgrind --tool=memcheck --leak-check=full ./testvalgrind --tool=memcheck --leak-check=full --show-reachable=yes --trace-children=yes ./filename其中--leak-check=full指的是完全检查内存泄漏,--show-reachable=yes是显示内存泄漏的地点,--trace-children=yes是跟入子进程。
如果您的程序是会正常退出的程序,那么当程序退出的时候valgrind自然会输出内存泄漏的信息。如果您的程序是个守护进程,那么也不要紧,我们 只要在别的终端下杀死memcheck进程(因为valgrind默认使用memcheck工具,就是默认参数--tools=memcheck)
参数选择
-tool=<name> 最常用的选项。运行 valgrind中名为toolname的工具。默认memcheck。memcheck ------> 这是valgrind应用最广泛的工具,一个重量级的内存检查器,能够发现开发中绝大多数内存错误使用情况,比如:使用未初始化的内存,使用已经释放了的内存,内存访问越界等。callgrind ------> 它主要用来检查程序中函数调用过程中出现的问题。cachegrind ------> 它主要用来检查程序中缓存使用出现的问题。helgrind ------> 它主要用来检查多线程程序中出现的竞争问题。massif ------> 它主要用来检查程序中堆栈使用中出现的问题。extension ------> 可以利用core提供的功能,自己编写特定的内存调试工具-h –help 显示帮助信息。-version 显示valgrind内核的版本,每个工具都有各自的版本。-q –quiet 安静地运行,只打印错误信息。-v –verbose 更详细的信息, 增加错误数统计。-trace-children=no|yes 跟踪子线程? [default: no]-track-fds=no|yes 跟踪打开的文件描述?[default: no]-time-stamp=no|yes 增加时间戳到LOG信息? [default: no]-log-fd=<number> 输出LOG到描述符文件 [2=stderr]-log-file=<file> 将输出的信息写入到filename.PID的文件里,PID是运行程序的进行ID-log-file-exactly=<file> 输出LOG信息到 file-log-file-qualifier=<VAR> 取得环境变量的值来做为输出信息的文件名。 [none]-log-socket=ipaddr:port 输出LOG到socket ,ipaddr:portLOG信息输出-xml=yes 将信息以xml格式输出,只有memcheck可用-num-callers=<number> show <number> callers in stack traces [12]-error-limit=no|yes 如果太多错误,则停止显示新错误? [yes]-error-exitcode=<number> 如果发现错误则返回错误代码 [0=disable]-db-attach=no|yes 当出现错误,valgrind会自动启动调试器gdb。[no]-db-command=<command> 启动调试器的命令行选项[gdb -nw %f %p]
设计思路:根据软件的内存操作维护一个有效地址空间表和无效地址空间表(进程的地址空间)
相关推荐视频
实时线上内存泄漏检测的4种实现方式,每一种都很重要
linux内存管理问题-如何理出自己的思路出来,开发与面试双丰收
学习地址:c/c++ linux服务器开发/后台架构师
需要C/C++ Linux服务器架构师学习资料加群812855908获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享

5.2、多个工具
1、Memcheck
最常用的工具,用来检测程序中出现的内存问题,所有对内存的读写都会被检测到,一切对malloc()/free()/new/delete的调用都会被捕获。所以,Memcheck 工具主要检查下面的程序错误
能够检测:
-
使用未初始化的内存 (Use of uninitialised memory)
-
使用已经释放了的内存 (Reading/writing memory after it has been free’d)
-
使用超过 malloc分配的内存空间(Reading/writing off the end of malloc’d blocks)
-
对堆栈的非法访问 (Reading/writing inappropriate areas on the stack)
-
申请的空间是否有释放 (Memory leaks – where pointers to malloc’d blocks are lost forever)
-
malloc/free/new/delete申请和释放内存的匹配(Mismatched use of malloc/new/new [] vs free/delete/delete [])
-
src和dst的重叠(Overlapping src and dst pointers in memcpy() and related functions)
-
重复free

Callgrind
和gprof类似的分析工具,但它对程序的运行观察更是入微,能给我们提供更多的信息。和gprof不同,它不需要在编译源代码时附加特殊选项,但加上调试选项是推荐的。Callgrind收集程序运行时的一些数据,建立函数调用关系图,还可以有选择地进行cache模拟。在运行结束时,它会把分析数据写入一个文件。callgrind_annotate可以把这个文件的内容转化成可读的形式。
Cachegrind
Cache分析器,它模拟CPU中的一级缓存I1,Dl和二级缓存,能够精确地指出程序中cache的丢失和命中。如果需要,它还能够为我们提供cache丢失次数,内存引用次数,以及每行代码,每个函数,每个模块,整个程序产生的指令数。这对优化程序有很大的帮助。
Helgrind
它主要用来检查多线程程序中出现的竞争问题。Helgrind寻找内存中被多个线程访问,而又没有一贯加锁的区域,这些区域往往是线程之间失去同步的地方,而且会导致难以发掘的错误。Helgrind实现了名为“Eraser”的竞争检测算法,并做了进一步改进,减少了报告错误的次数。不过,Helgrind仍然处于实验阶段。
Massif
堆栈分析器,它能测量程序在堆栈中使用了多少内存,告诉我们堆块,堆管理块和栈的大小。Massif能帮助我们减少内存的使用,在带有虚拟内存的现代系统中,它还能够加速我们程序的运行,减少程序停留在交换区中的几率。
5.3、使用原理



Memcheck 能够检测出内存问题,关键在于其建立了两个全局表。
1、Valid-Value 表:
对于进程的整个地址空间中的每一个字节(byte),都有与之对应的 8 个 bits;对于 CPU 的每个寄存器,也有一个与之对应的 bit 向量。这些 bits 负责记录该字节或者寄存器值是否具有有效的、已初始化的值。
2、Valid-Address 表
对于进程整个地址空间中的每一个字节(byte),还有与之对应的 1 个 bit,负责记录该地址是否能够被读写。
检测原理:
-
当要读写内存中某个字节时,首先检查这个字节对应的 A bit。如果该A bit显示该位置是无效位置,memcheck 则报告读写错误。
-
内核(core)类似于一个虚拟的 CPU 环境,这样当内存中的某个字节被加载到真实的 CPU 中时,该字节对应的 V bit也被加载到虚拟的 CPU 环境中。一旦寄存器中的值,被用来产生内存地址,或者该值能够影响程序输出,则 memcheck 会检查对应的V bits,如果该值尚未初始化,则会报告使用未初始化内存错误。
5.4、具体使用
1. 使用未初始化的内存(使用野指针)
这里我们定义了一个指针p,但并未给他开辟空间,即他是一个野指针,但我们却使用它了

Valgrind检测出我们程序使用了未初始化的变量,但并未检测出内存泄漏。

2.在内存被释放后进行读/写(使用野指针)
p所指向的内存被释放了,p变成了野指针,但是我们却继续使用这片内存。

Valgrind检测出我们使用了已经free掉的内存,并给出这片内存是哪里分配哪里释放的。

3.从已分配内存块的尾部进行读/写(动态内存越界)
我们动态地分配了一段数组,但我们在访问个数组时发生了越界读写,程序crash掉。

Valgrind检测出越界的位置。

注意:Valgrind不检查静态分配数组的使用情况!所以对静态分配的数组,Valgrind表示无能为力!比如下面的例子,程序crash掉,我们却不知道为什么。


4.内存泄漏
内存泄漏的原因在于没有成对地使用malloc/free和new/delete,比如下面的例子。

Valgrind会给出程序中malloc和free的出现次数以判断是否发生内存泄漏,比如对上面的程序运行memcheck,Valgrind的记录显示上面的程序用了1次malloc,却调用了0次free,明显发生了内存泄漏!

上面提示了我们可以使用–leak-check=full进一步获取内存泄漏的信息,比如malloc和free的具体行号。

5. 不匹配地使用malloc/new/new[] 和 free/delete/delete[]
正常使用new/delete和malloc/free是这样子的:


而不匹配地使用malloc/new/new[] 和 free/delete/delete[]则会被提示mismacth:


6.两次释放内存
double free的情况同样是根据malloc/free的匹配对数来体现的,比如free多了一次,Valgrind也会提示。


相关文章:
内存泄漏的原因,内存泄漏如何避免?内存泄漏如何定位?
1. 内存溢出 内存溢出 OOM (out of memory),是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个int,但给它存了long才能存下的数,那就是内存溢出。 2. 内存泄…...
关于全志T113开发板接7寸LCD屏幕显示异常问题的解决方案
在入手全志T113之后,第一时间移植好了之前6ull平台的rootfs。但是在测试QT的过程中发现屏幕最右侧有一部分显示不正常,经过初步推测应该是RGB行场同步时序有问题。本以为在设备树里面稍作修改之后就能OK,但是居然前前后后一共花了至少三个星期…...
SpringMVC第四阶段:Controller中如何接收请求参数
Controller中如何接收请求参数 1、原生API参数类型 1.1、HttpServletRequest类 只需要在Controller的目标方法中, 直接写上HttpServletRequest对象即可获取 原生API的 request对象实例。 RequestMapping(value "/p1") public String param1(HttpServletRequest …...
第三十回: LisvtView响应事件
我们在上一章回中介绍了如何给ListView添加分隔线,本章回中将介绍ListView响应事件相关的知识.闲话休提,让我们一起Talk Flutter吧。 概念介绍 我们在这里说的ListView响应事件主要分两种类型: 一种是滑动ListView时ListView做出响应,我们…...
重磅!用友荣登全球5强
近日,全球权威信息技术研究和顾问公司Gartner发布《Market Share: All Software Markets, Worldwide,2022》报告,用友在EAM(资产管理)市场再创新高,市场占有率位居全球第五位,亚太第一位&#x…...
计算机组成原理实验报告二-认识汇编语言
实验资料: https://wwpv.lanzoue.com/b05drqjef 密码:d19t 使用txt文档编写下面C源码,文档命名为【学号_hello.c】并使用Mingw工具(是 Minimalist GNU for Windows的缩写)的bin文件夹下gcc.exe带选项编译()…...
都说计算机今年炸了,究竟炸到什么程度呢?
近期,最大的计算机领域新闻莫过于Intel CPU严重漏洞曝光。该漏洞被称为“Meltdown”和“Spectre”,几乎涵盖了所有使用Intel芯片的计算机,包括PC、笔记本电脑、服务器和移动设备。 Meltdown漏洞的主要风险是黑客可以利用此漏洞访问操作系统的…...
0Ω的电阻作用
0欧姆电阻即电阻标值为0欧姆的电阻,多用于PCB设计等方面,是一种理想电阻。那0欧姆电阻是表示没有电阻吗?当然不是,0欧姆电阻的阻值不是0欧姆,只是接近0欧姆。 1、调试方便或者兼容设计:可以选择器件、功能…...
02 PostGIS常用空间分析函数
常用的PostGIS空间分析函数清单: 序号函数名描述示例1ST_AsText(geometry)将几何对象转换为文本形式的WKT字符串ST_AsText(ST_GeomFromText(‘POINT(1 2)’)) 返回 ‘POINT(1 2)’2ST_GeometryType(geometry)返回几何对象的类型,如POINT、LINESTRING、P…...
[Golang] 管理日志信息就用Zap包
😚一个不甘平凡的普通人,致力于为Golang社区和算法学习做出贡献,期待您的关注和认可,陪您一起学习打卡!!!😘😘😘 🤗专栏:算法学习 &am…...
【pytest】执行环境切换的两种解决方案
一、痛点分析 在实际企业的项目中,自动化测试的代码往往需要在不同的环境中进行切换,比如多套测试环境、预上线环境、UAT环境、线上环境等等,并且在DevOps理念中,往往自动化都会与Jenkins进行CI/CD,不论是定时执行策略…...
2023国赛tomcat题
环境: 10.10.120.128 安装 tomcaA 10.10.120.129 安装tomcatB 10.10.120.130 安装 nginx 配置dns: 正向解析 反向解析 Tomcat ssl配置 [root@localhost ~]# tar -zxvf jdk-11.0.8_linux-x64_bin.tar.gz [root@localhost ~]# mv jdk-11.0.8 /usr/local/ Vim /etc/profile …...
计算机视觉——day 92 基于跨领域协作学习的单图像去雨
基于跨领域协作学习的单图像去雨 1. Introduction3. Proposed method3.1 网络架构 4. Experiments and results4.1 数据集和指标4.3 合成图像的结果4.8 建模复杂度和运行时间 5. Conclusion 1. Introduction 深度卷积神经网络(DCNN)在图像解析任务中取得了优异的性能。然而&am…...
Java 正则表达式
Java 正则表达式 正则表达式定义了字符串的模式。 正则表达式可以用来搜索、编辑或处理文本。 正则表达式并不仅限于某一种语言,但是在每种语言中有细微的差别。 示例 boolean b String.matches(String regex) ;regex 有2种形式: 字符串 直接就是一…...
Oracle索引知识看这一篇就足够
🏆 文章目标:本篇介绍Oracle索引知识以及案例场景 🍀 Oracle索引知识看这一篇就足够 ✅ 创作者:Jay… 🎉 个人主页:Jay的个人主页 🍁 展望:若本篇讲解内容帮助到您,请帮忙…...
kafka命令行操作
新老版本kafka命令行操作 啓動Kafka: kafka-server-start.sh -daemon $KAFKA_HOME/config/server.properties Kafka命令行操作 查看当前集群中已存在的主题topic 旧的方式 kafka-topics.sh --zookeeper bdphdp01:2181 --list kafka-topics.sh --zookeeper bdphd…...
Pinia 上手使用(store、state、getters、actions)
参考链接:https://juejin.cn/post/7121209657678364685 Pinia官方:https://pinia.vuejs.org/zh/introduction.html 一、安装 npm i pinia -S二、main.js 引入 import { createApp } from "vue" import App from "./App.vue" impor…...
C++小项目之文本编辑器mynote(1.0.0版本)
2023年5月19日,周五晚上: 今天晚上突然想写一个运行在命令行上的文本编辑器,因为平时写文本时老是要创建新的文本文件,觉得太麻烦了。捣鼓了一个晚上,才选出一个我觉得比较满意的。我把这个程序添加到了系统环境变量中…...
人工智能的界面革命,消费者与企业互动的方式即将发生变化。
本文来源于 digitalnative.substack.com/p/ais-interface-revolution 描述了一种社会现象: 随着真实友谊的减少和虚拟友谊的增加,越来越多的人开始将AI聊天机器人视为自己的朋友,甚至建立了深厚的情感纽带。这可能与当前人们越来越孤独的现实…...
深度学习课程:手写体识别示例代码和详细注释
Pytorch 的快速入门,参见 通过两个神经元的极简模型,清晰透视 Pytorch 工作原理。本文结合手写体识别项目,给出一个具体示例和直接关联代码的解释。 1. 代码 下面代码展示了完整的手写体识别的 Python 程序代码。代码中有少量注释。在本文后…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...
Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
Bean 作用域有哪些?如何答出技术深度?
导语: Spring 面试绕不开 Bean 的作用域问题,这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开,结合典型面试题及实战场景,帮你厘清重点,打破模板式回答,…...
WebRTC从入门到实践 - 零基础教程
WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC? WebRTC(Web Real-Time Communication)是一个支持网页浏览器进行实时语音…...
