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

这篇具有很好参考价值的文章主要介绍了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)对于大型项目,可以使用静态代码分析工具

像开源的有codechecker软件,集成了一些静态代码分析的工具

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

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

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

代码静态分析(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的次数,以及被申请的字节数,在每一个内存泄漏的地方,也会显示函数调用堆栈,便于追踪:
C/C++内存泄漏原因分析与应对方法,C++学习笔记,c语言,c++,操作系统底层
(注:图片相关函数做了打码处理)

这个工具的用法还挺多,可以参考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的断点命中次数,如果次数一样,那没问题。文章来源地址https://www.toymoban.com/news/detail-564355.html

到了这里,关于C/C++内存泄漏原因分析与应对方法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 金浪路由器遭入侵的原因分析及防范方法

        一、通过分析发现,这些数据包是内网电脑A发送给外网电脑B的,发送速度不小于每秒l万个,将该电脑断开后,问题解决,这时我们可以在电脑A上安装Sniffer,通过抓包分析发现,一旦连接网络,该电脑主动攻击外网电脑B,原来,这台电脑成了一次攻击的傀儡机。 二、攻

    2024年02月05日
    浏览(45)
  • 首次设置贝尔金无线路由出错原因分析与解决方法

      一、检查线路、拓扑连接和面板灯 先用一台电脑拨号上网,如果行,可以排除线路故障。之后只将一台电脑接到无线路由器,路由器接MODEM,检查无线路由器电源接口、广域网端口和局域网端口连接情况和对应的面板灯,在物理连接正常时,面板的电源灯、Internet灯和已用

    2024年02月05日
    浏览(40)
  • 阿尔法路由器无法Telnet的原因分析以及解决方法

      一、组网环境 在阿尔法路由器的组网环境中,当配置完成后,所有的业务运行正常,通过外网或者阿尔法A上可以Ping通阿尔法B,但是无法Telnet到阿尔法B,如果先从外网登陆上,则能够Telnet到路由器。 二、故障分析 1、分析Telnet与Ping的区别在于:Ping可以直接在阿尔法B接口

    2024年02月05日
    浏览(38)
  • 飞鱼星路由器无线传感器故障的原因分析及解决方法

    当网络或系统出现故障时,网络故障管理便成为管理员首要用到的工具,因此,故障管理事实上是整个网络管理的重中之重,由于网络故障涉及到不同厂商,不同类型设备,涉及复杂的网络拓扑结构,涉及不同组织对故障类型的不同定位规则,本文就以飞鱼路由器为大家无线

    2024年02月05日
    浏览(51)
  • 网上邻居打开很慢(默认会延迟30秒)的原因分析及解决方法

    有很多使用windows XP用户反映在打开网上邻居很慢,开机也没有开太多的运行程序,怎么会感觉浏览网上邻居很慢呢?其实这只是系统的一种设置,下面来看看打开网上邻居很慢的原因以及解决途径。 一、网上邻居打开很慢的原因 在WinXP和Win2000中浏览网上邻居时系统默认会延

    2024年02月06日
    浏览(45)
  • Ping 出现TTL expired in transit错误原因分析及解决方法

    寝室网络割接导致本本上不了网了,于是在其他地方ping本本的IP来试,结果出现了TTL expired in transit的提示,这种提示并不常见,如下图所示: ping 和 tracert 的结果 用tracert来看路由状况,原来是出现路由环路导致TTL超时。 出现路由环路的解决方法是重新设定路由配置,避免环

    2024年02月06日
    浏览(41)
  • FANUC机器人SRVO-105和SRVO-067故障报警原因分析及处理方法

    如下图所示,公司的一台机器人在正常工作时突然报警SRVO-105门打开或紧急停止,同时还有SRVO-067 OHAL2报警(G:1 A:2),按Reset键无法消除报警, 那么遇到这种情况,首先,我们来看一下报警说明书上的解释: 首先看一下SRVO-105报警: 总结: 报警原因:控制装置的柜门打开

    2024年02月08日
    浏览(88)
  • 常见的内存泄漏原因和解决方案

    1.全局引用 问题:在JavaScript代码中,使用全局变量或全局对象来保存对DOM元素或其他对象的引用,这可能导致内存泄漏。 解决方案:避免使用全局变量或全局对象,改用合适的作用域来管理变量和对象的生命周期。确保在不再需要时正确地释放这些引用。 2.事件监听器 问题

    2024年02月13日
    浏览(45)
  • Handler内存泄漏原因及解决方案

    在Activity中,将Handler声明成非静态内部类或匿名内部类,这样Handle默认持有外部类Activity的引用。如果Activity在销毁时,Handler还有未执行完或者正在执行的Message,而Handler又持有Activity的引用,导致GC无法回收Activity,导致内存泄漏。如以下两种情形可能导致内存泄漏 1、在Act

    2024年02月16日
    浏览(57)
  • QT--崩溃原因分析

    本文为学习记录,若有错误,请联系作者,谦虚受教。 你从来来去自由,若你不想要了跑开便是。 发布的客户版本里分析崩溃原因,便于解决问题。 在自己QT安装的目录下,例如:D:QtQt5.12.3Toolsmingw730_32bin,找到adde2line.exe。 将add2line.exe复制到自己发布的版本中。 在代码

    2024年02月13日
    浏览(57)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包