C/C++内存泄漏原因分析与应对方法

内存泄漏

一、内存泄漏的危害:

内存泄漏会导致当前应用程序消耗更多的内存,使得其他应用程序可用的内存更少了。

如果有个进程可用的内存不够,就会触发Linux操作系统的直接/后台内存回收(即将一些内存页的数据写到磁盘里,那么该页也就可用了,脏页回写)。虽然后台回收是异步的不阻塞当前进程,但是内存还是不够会触发直接内存回收,最后内存泄漏积累到一定程度,会直接触发OOM,该机制会杀掉那些实时占用内存大的进程

而且,即使没有OOM,无论是直接回收还是后台回收,都需要磁盘I/O而且需要多执行额外的回收线程,使系统性能下降。

  • 后台内存回收(kswapd):在物理内存紧张的时候,会唤醒 kswapd 内核线程来回收内存,这个回收内存的过程异步的,不会阻塞进程的执行。
  • 直接内存回收(direct reclaim):如果后台异步回收跟不上进程内存申请的速度,就会开始直接回收,这个回收内存的过程是同步的,会阻塞进程的执行,这个过程比较慢,导致CPU占用率飙升

如果直接内存回收后,空闲的物理内存仍然无法满足此次物理内存的申请,那么内核就会放最后的大招了 ——触发 OOM (Out of Memory)机制

还有资源泄漏:

比如没有关闭文件,程序提前return或报错或忘记关闭,则可能导致想写入文件的数据没有真正落盘,从而丢失数据。

二、内存泄漏举例:

1,在free()前就返回了,或者是报错并退出程序。要在程序的所有路径上(if()的各个条件)都执行资源释放操作。

2,在析构函数中未执行内存释放操作。在构造函数中申请了堆内存或者打开了文件,在析构函数中忘了释放资源。

3,基类的析构函数未声明为虚函数

析构函数如果不声明为虚函数,可能会导致多态对象在删除时无法正确调用派生类的析构函数(如果子类构造函数里malloc()了内存,然后在析构函数里free()),从而导致内存泄漏。

4,shared_ptr循环引用导致内存泄漏,用weak_ptr解决。如下示例:

class Contro {
private:double* p;public:Contro() {p = new double[10];}~Contro() {delete[] p;std::cout << "in ~Contro" << std::endl;}
// 类内类class SubContro {public:SubContro() {p = new double[10];}~SubContro() {delete[] p;std::cout << "in ~SubContro" << std::endl;}std::shared_ptr<Contro> controller_;};std::shared_ptr<SubContro> sub_controller_;
};int main() {auto contro = std::make_shared<Contro>();auto sub_contro = std::make_shared<Contro::SubContro>();contro->sub_controller_ = sub_contro;sub_contro->controller_ = contro;// 打印引用计数std::cout << "contro use_count: " << contro.use_count() << std::endl;std::cout << "sub_contro use_count: " << sub_contro.use_count() << std::endl;return 0;
}

发生循环引用,两个的引用计数输出都是2,所以main函数结束的时候,引用计数没有减为0,就不会调用二者的析构函数,导致资源泄漏
将SubContro类里的shared_ptr改成weak_ptr即可,后者不会增加引用计数,因此两个智能指针的引用计数都是1,然后main结束的时候,引用计数减少为0,然后执行析构函数,此时不会发生内存泄漏,输出如下:

contro use_count: 1
sub_contro use_count: 2
in ~Contro
in ~SubContro

三、避免内存泄漏的手段:

1. 静态代码检查工具

(1)对于大型项目,可以使用啄木鸟这种静态代码分析工具

静态代码检查工具会从词法、语法、语义等多维度去对工程代码扫描分析,发现可能存在的问题,比如变量未定义、类型不匹配、变量作用域问题、数组下标越界、内存泄露等问题。

既然是静态,那么就不是运行时。但是是编译前还是编译后还是编译中?

其实都有,啄木鸟是给源文件就行,然后它会在编译的过程中去检测语法,词法,以及最后生成的二进制。

代码静态分析(SAST):可以简单的理解为在不执行程序的情况下,对源代码, 中间代码或者二进制代码进行分析的技术

(2)编译成专门的内存泄漏检查版本。

可以把整个项目编译成检查内存泄漏版本的可执行文件,然后运行相关工具,并且让运行结果专门记录内存泄漏,将泄漏结果放在对应输出文件上。

比如opengauss就有,参考链接:http://t.csdn.cn/DqusO

编译openGauss时,编译一个memcheck版的,然后通过跑fastcheck_single来发现代码中的内存问题。 编译方式和编译普通的openGauss基本一致,只是在configure时,添加一个 --enable-memory-check 参数,编译出来的就是memcheck版本的openGauss。

但是编译前,要设置一些环境变量,ulimit -v unlimited

ulimit命令:用于控制shell程序的资源, -v <虚拟内存大小>  指定可使用的虚拟内存上限,单位为KB。

因为可能有内存泄漏,所以就设置虚拟内存大小为不受限制。

2. valgrind工具

可以安装valgrind工具,指定工具--tool=memcheck,也可以指定输出日志,否则输出在终端

--log-file=leak1.log

对可执行文件a.out,执行如下命令:

valgrind --log-file=valgrind.log --tool=memcheck --leak-check=full --show-leak-kinds=all ./a.out

如下可以看到总的malloc和free的次数,以及被申请的字节数,在每一个内存泄漏的地方,也会显示函数调用堆栈,便于追踪:
在这里插入图片描述
(注:图片相关函数做了打码处理)

这个工具的用法还挺多,可以参考https://zhuanlan.zhihu.com/p/92074597

3. GDB调试

比如我们怀疑FUNC()函数有内存泄漏。

1,比如给某个函数FUNC()打断点,进入后这个函数里面也调用了很多其他函数func1,func2…,怀疑这些调用里面,或者外面有内存泄漏。我们可以给malloc()和free()打断点(或者是自己封装的函数),当malloc()命中后,bt查看栈帧,就知道哪个函数调用了malloc,申请了堆内存,比如func1,这样可以重点关注该函数。

2,然后看后面free()断点有没有命中,命中的时候查看栈帧,如果不是这个函数func1调用的free(),那说明这个函数没有执行free。

3,此外,可以追踪指针p的值(watch p),看看它有没有变为0x0,被释放且被赋值为0x0,才不会成为悬空指针。

4,在函数FUNC()的末尾,还可以看看malloc和free的断点命中次数,如果次数一样,那没问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1343.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

springboot服务端接口公网远程调试,并实现HTTP服务监听

文章目录 前言1. 本地环境搭建1.1 环境参数1.2 搭建springboot服务项目 2. 内网穿透2.1 安装配置cpolar内网穿透2.1.1 windows系统2.1.2 linux系统 2.2 创建隧道映射本地端口2.3 测试公网地址 3. 固定公网地址3.1 保留一个二级子域名3.2 配置二级子域名3.2 测试使用固定公网地址…

Argo CD 入门扫盲使用

目录 一、什么是 argo cd 二、为什么使用 argo cd 三、argo cd 架构图 四、Argo CD 使用 1、安装 Argo CD 2、安装 Argo CD CLI 3、发布 Argo CD 服务 4、获取 Argo CD 密码 5、准备 Git 仓库 6、创建 Argo CD App 7、版本升级 8、版本回滚 一、什么是 argo cd A…

数据结构(王道)——线性表的存储结构之循环表

一、循环单链表 定义&#xff1a; 循环单链表代码实现 创建并初始化、判断循环单链表是否为空、判断结点p是否为循环单链表的表尾结点的代码操作。 二、循环双链表 定义&#xff1a; 循环双链表代码实现 创建并初始化、判断循环双链表是否为空、判断结点p是否为循环双链表的…

JVM重点整理

一、虚拟机架构图 二、类加载过程 类加载器的作用&#xff1a;负责把class文件加载到内存中 类加载过程&#xff1a; 加载&#xff1a; 通过类的全限定名获取此类的二进制字节流文件的编码结构---->运行时的内存结构内存中生成一个class对象 链接&#xff1a; 验证&#x…

智能电表远程抄表系统原理

智能电表远程抄表系统是现代智能电网建设的重要组成部分&#xff0c;它利用物联网技术实现电表数据的远程采集、传输和处理&#xff0c;提高了电力公司的抄表效率&#xff0c;同时也为用户提供了更加便捷、准确的用电服务。本文将从远程智能电表抄表系统的工作原理、特点、应用…

每天一道C语言编程:排队买票

题目描述 有M个小孩到公园玩&#xff0c;门票是1元。其中N个小孩带的钱为1元&#xff0c;K个小孩带的钱为2元。售票员没有零钱&#xff0c;问这些小孩共有多少种排队方法&#xff0c;使得售票员总能找得开零钱。注意&#xff1a;两个拿一元零钱的小孩&#xff0c;他们的位置互…

精益生产有哪些管理工具?

精益生产有哪些管理工具&#xff1f; 一、什么是精益生产 智能制造是落实我国制造强国战略的重要举措&#xff0c;加快推进智能制造&#xff0c;是加速我国工业化和信息化深度融合、推动制造业供给侧结构性改革的重要着力点&#xff0c;对重塑我国制造业竞争新优势具有重要意义…

优化类问题建模解析

模型建立阶段 线性规划模型&#xff1a;目标函数和约束条件均为线性 整数规划或0-1规划&#xff1a;决策变量取值被限制为整数或0、1 动态优化模型&#xff1a;以时间为划分阶段的动态过程优化问题 非线性规划模型&#xff1a;目标函数或约束条件中包括非线性函数 多目标规划模…

超高性能协议框架fury完爆protostuff(附性能测试对比)

简单介绍: 序列化框架是系统通信的基础组件&#xff0c;在大数据、AI 框架和云原生等分布式系统中广泛使用。当对象需要跨进程、跨语言、跨节点传输、持久化、状态读写、复制时&#xff0c;都需要进行序列化&#xff0c;其性能和易用性影响运行效率和开发效率。 Fury 是一个基于…

SSH服务(二十六)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 一、概述 二、特点 三、身份验证机制 四、验证过程 五、加密机制 六、基本参数 ​七、 身份验证机制 八、基本操作 1. ssh 2. scp 3. sftp 4. 密钥对验证 总结 前…

人工智能商业变现途径,并介绍详细公司案列

目录 1. 推荐系统&#xff1a;2. 智能广告和营销&#xff1a;3. 聊天机器人和虚拟助手&#xff1a;4. 自动化和机器人化&#xff1a;5. 数据分析和预测&#xff1a;6. 机器视觉和图像识别&#xff1a;7. 金融科技&#xff08;FinTech&#xff09;&#xff1a;8. 医疗诊断和健康…

m4a文件出现损坏应该如何修复?

M4A文件&#xff0c;也称为Apple Lossless Encoder&#xff0c;是一种类似于MP4文件的音频格式。它们之间唯一的区别是M4A文件里面没有视频。作为一个音频文件&#xff0c;它已被广泛使用&#xff0c;常用的Windows&#xff0c;Media Player&#xff0c;Mac quicktime和iTunes等…

【MongoDB实战】数据备份与恢复(部分迁移)

场景&#xff1a; 需求&#xff1a; 解决方案&#xff1a; 步骤&#xff1a; Stage 1&#xff1a;【生产环境】修改备份文件映射 Stage 2&#xff1a;【生产环境】重新构建mongodb Stage 3&#xff1a;【客户环境】修改备份文件映射&#xff0c;同 Stage 1 Stage 4&…

九五从零开始的运维之路(其二十)

[TOC](文章目录) 文章目录 前言一、LAMP是什么二、配置环境及安装1.配置yum源2.关闭防火墙、网络图形化工具及SElinux3.安装软件包 三、配置apache服务器内容四、启动服务五、访问验证总结 前言 本篇将简述的内容&#xff1a;Linux系统下的LAMP平台部署 基于discuz框架的论坛搭…

用WooCommerce创建一个多用户商城系统和多供应商市场

线上市场是下一波数字化商务。2020 年&#xff0c;超过60% 的线上支出是通过数字市场发生的。人们喜欢从市场上购物&#xff0c;因为它们使购物变得容易。出于同样的原因&#xff0c;企业喜欢通过它们进行销售。通过多用户商城系统和多供应商WooCommerce商城设置&#xff0c;每…

kafka接收外部接口的数据,并实现转发

目录 一、什么是kafka 二、kafka接收外部接口数据 三、kafka收到数据后转发 四、kafka总结 一、什么是kafka Kafka是一种分布式流式处理平台&#xff0c;最初由LinkedIn开发。它设计用于高吞吐量、低延迟的数据处理&#xff0c;能够处理大规模的实时数据流。Kafka采用发布…

gitee 使用

1.打开git bash 2.cd 进入到合适位置 3.git clone 项目 4.配置用户名和email&#xff08;不然没法记录谁操作的&#xff09; pycharm &#xff08;ctrlk&#xff09;

个人微信机器人开发

微信机器人的主要职责是为了帮助群主来进行微信社群的管理&#xff0c;微信机器人当前主要分为两种&#xff0c;一种是微信聊天机器人&#xff0c;还有一种是微信群管理机器人。 微信聊天机器人的主要职责是可以在群内聊天&#xff0c;从而提高微信群活跃度&#xff1b;微信群管…

【Ceph集群应用】CephFS文件系统之MDS接口详解

CephFS文件系统之MDS接口详解 1.创建CephFS文件系统MDS接口1.1 创建cephfs1.2 基于内核的客户端挂载1.3 基于fuse工具方式的客户端挂载 接上文基于ceph-deploy部署Ceph集群详解 1.创建CephFS文件系统MDS接口 服务端操作 &#xff08;1&#xff09;在admin管理节点创建mds服务…

Java打怪升级路线的相关知识

第一关:JavaSE阶段 1、计算机基础 2、java入门学习 3、java基础语法 4、流程控制和方法 5、数组 6、面向对象编程 7、异常 8、常用类 9、集合框架 10、IO 11、多线程 12、GUI编程 13、网络编程 14、注解与反射 15、JUC编程 16、JVM探究 17、23种设计模式 18、数据结构与算法 1…