当前位置:网站首页 > Vue.js开发 > 正文

cjson库内存泄露(performselector内存泄露)



头像
⭐️个人主页:@小羊
⭐️所属专栏:C++
很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~

动图描述


什么是内存泄漏:内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。
内存泄漏的危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。

解决内存泄漏的问题,使用智能指针管理是一个很好的选择


RAII(Resource Acquisition Is Initialization)是C++中的一种资源管理技术,其核心思想是利用对象的生命周期来自动管理资源,因为对象的构造和析构是自动调用的。

在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:

  1. 不需要显式地释放资源
  2. 对象所需的资源在其生命期内始终保持有效

不管对象是生命周期正常结束还是抛了异常,最后都会自动释放掉:

在这里插入图片描述

 

上面的还不能称为智能指针,因为它还不具有指针的行为。指针可以解引用,也可以通过->去访问所指空间中的内容,因此智能指针模板类中还得需要将* 、->重载下,才可让其像指针一样去使用。

 

智能指针的原理:RAII特性 重载operator*和operator->,具有像指针一样的行为

这么看智能指针好像还挺好理解挺简单的,不过先别高兴太早,智能指针麻烦的地方不在这里,在拷贝构造。
我们知道C++默认构造函数实现的是浅拷贝,而智能指针模拟的是原生指针的行为,我们期望它的拷贝就是浅拷贝,看似类的默认构造函数就很好的满足我们的需求,但是不要忘了让多个指针指向同一块空间,这样会导致同一块空间出现析构多次的情况,显然这里出现了矛盾。


  • C++智能指针都在头文件中定义。

要求其对“裸”指针的完全占有性,即一个“裸”指针不能同时被两个以上的所拥有。
虽然提供了拷贝,但它的拷贝是一种管理权转移。被拷贝对象会失去资源的所有权,拷贝对象会接管这个资源。同样的赋值也会转移管理权。

在这里插入图片描述

 

由于存在上述限制和潜在问题,C++11及以后的版本引入了更先进的智能指针,如和,它们提供了更强大和灵活的资源管理功能。因此,在现代C++编程中,建议使用这些新的智能指针来替代。


不支持拷贝,其拷贝构造函数被禁掉了。

在这里插入图片描述

 
 

如果管理的是多个连续的空间,则释放时会出错,因为它的底层是,而释放连续的空间需要。

在这里插入图片描述

解决这个问题可以通过定制删除器来解决。

在这里插入图片描述
定制删除器:

 

在这里插入图片描述


我们重点学习。
的原理:通过引用计数的方式来实现多个对象之间共享资源。

  1. 在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共享
  2. 在对象被销毁时,就说明自己不使用该资源了,对象的引用计数减一
  3. 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源
  4. 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了

在这里插入图片描述

类似,也支持传定制删除器,但它们两个支持传定制删除器的位置有所不同。

在这里插入图片描述

另外,除了直接对象,也可以用构造对象。它是一个函数模板:

在这里插入图片描述
在这里插入图片描述

一次性完成了内存分配和对象构造,因此可以减少内存分配的次数,还可以使得控制块和对象可以分配在同一块连续的内存上,减少了内存碎片化的风险。


| 接下来模拟实现shared_ptr:

第一步:实现出RAII的框架

 

第二步:如何实现引用计数

首先我们应该讨论引用计数保存在哪里,不能存在各自的对象中,应该满足一个资源配一个计数,也就是公共计数。静态成员变量计数也不行,因为静态成员变量不属于某个对象,而是属于类的所有对象,这显然是不行的,因为所有的对象不可能都指向同一资源,可能其中的几个对象分别指向不同的资源。

这里合理的处理是将引用计数开在堆上(也就是上面部分提到的控制块),然后在对象中存一个指针指向这个计数。

在这里插入图片描述

一个资源配一个计数,所以计数也在构造的时候给出。

 

第三步:赋值重载

赋值需要注意的是被赋值的指针原来管理的资源是否需要释放。那么这里就要显示地调用析构函数,不过析构函数最好和构造等一一对应,所以这里可以将析构的逻辑用一个函数重新包装,然后在赋值和析构函数中调用这个函数处理。
其中赋值还需要防止自己给自己赋值的情况,有可能会出现野指针的问题。

在这里插入图片描述

 

第四步:加定制删除器

在构造时我们要传一个可调用对象过去,这个对象可能是函数指针,可能是仿函数,也有可能是,这里不确定接收的类型,就可以用来接收。这里的删除器只供构造函数使用,因此不能传整个类模版。

 

成员变量需要一个缺省值,如果没有传定制的删除器需要用默认的删除操作进行释放。


第五步:解决循环引用的问题

在特殊场景,比如双向循环链表中,如果两个节点互相指向,就会出现循环引用的问题,最后导致内存泄漏

 

在这里插入图片描述

这里内存泄漏的关键是:n2后定义的先析构,而n2指向的资源还有管理,所以n2指向的资源这里还不会释放;接下来析构n1指向的资源,而n1指向的资源还有管理,所以n1指向的资源这里也还不会释放。最后n1和n2两个都释放了它们原本指向的资源还得不到释放,因为还有这两个资源内部的互相管理者。

为了处理这种情况的发生,出现了来配合解决这个问题。


用构造不增加引用计数。

当然这里只实现了一个简单的。

 
 

本篇文章的分享就到这里了,如果您觉得在本文有所收获,还请留下您的三连支持哦~

头像
到此这篇cjson库内存泄露(performselector内存泄露)的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!

版权声明


相关文章:

  • js深拷贝和浅拷贝的方法区别(js深拷贝和浅拷贝如何实现)2025-02-10 14:18:08
  • map转json数组(map转json字符串)2025-02-10 14:18:08
  • vue ui怎么安装(vue安装步骤)2025-02-10 14:18:08
  • 本地回环地址(本地回环地址是只有127.0.0.1这一个吗)2025-02-10 14:18:08
  • de4dot反编译vue(vue 反编译)2025-02-10 14:18:08
  • vue安装脚手架 检查版本(vue3.0脚手架安装)2025-02-10 14:18:08
  • pcie5.0 4.0(pcie5.0 4.0 3.0 游戏)2025-02-10 14:18:08
  • vuejs插槽(vue3.0 插槽)2025-02-10 14:18:08
  • 卡巴斯基更新源(卡巴斯基7.0)2025-02-10 14:18:08
  • pcie5.0能插pcie4.0么(pcie5.0有什么用)2025-02-10 14:18:08
  • 全屏图片