内存泄漏的原因,内存泄漏如何避免?内存泄漏如何定位?
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 程序代码。代码中有少量注释。在本文后…...

智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
django blank 与 null的区别
1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是,要注意以下几点: Django的表单验证与null无关:null参数控制的是数据库层面字段是否可以为NULL,而blank参数控制的是Django表单验证时字…...
LOOI机器人的技术实现解析:从手势识别到边缘检测
LOOI机器人作为一款创新的AI硬件产品,通过将智能手机转变为具有情感交互能力的桌面机器人,展示了前沿AI技术与传统硬件设计的完美结合。作为AI与玩具领域的专家,我将全面解析LOOI的技术实现架构,特别是其手势识别、物体识别和环境…...

Unity中的transform.up
2025年6月8日,周日下午 在Unity中,transform.up是Transform组件的一个属性,表示游戏对象在世界空间中的“上”方向(Y轴正方向),且会随对象旋转动态变化。以下是关键点解析: 基本定义 transfor…...

Matlab实现任意伪彩色图像可视化显示
Matlab实现任意伪彩色图像可视化显示 1、灰度原始图像2、RGB彩色原始图像 在科研研究中,如何展示好看的实验结果图像非常重要!!! 1、灰度原始图像 灰度图像每个像素点只有一个数值,代表该点的亮度(或…...

【iOS】 Block再学习
iOS Block再学习 文章目录 iOS Block再学习前言Block的三种类型__ NSGlobalBlock____ NSMallocBlock____ NSStackBlock__小结 Block底层分析Block的结构捕获自由变量捕获全局(静态)变量捕获静态变量__block修饰符forwarding指针 Block的copy时机block作为函数返回值将block赋给…...