高并发内存池(回收)[4]

threadcache还给centralcache

void ThreadCache::Deallocate(void* ptr, size_t size)
{assert(ptr);assert(size <= MAX_BYTES);// 找对映射的自由链表桶,对象插入进入size_t index = SizeClass::Index(size);_freeLists[index].Push(ptr);// 当链表长度大于一次批量申请的内存时就开始还一段list给central cacheif (_freeLists[index].Size() >= _freeLists[index].MaxSize()){ListTooLong(_freeLists[index], size);}
}void ThreadCache::ListTooLong(FreeList& list, size_t size)
{void* start = nullptr;void* end = nullptr; list.PopRange(start, end, list.MaxSize());CentralCache::GetInstance()->ReleaseListToSpans(start, size);
}

centralcache回收

函数CentralCache::ReleaseListToSpans的实现,用于释放一组内存块到对应的Span中。

函数的输入参数是startsize,分别表示要释放的内存块的起始地址和大小。

函数的主要逻辑如下:

  1. 根据内存块的大小size计算出对应的SizeClass的索引index
  2. 获取对应索引的SpanList,并对其进行加锁。
  3. 进入一个循环,遍历要释放的内存块链表,直到遍历完所有内存块。
  4. 在循环中,首先获取当前内存块的下一个内存块地址next
  5. 根据当前内存块的地址start,通过PageCache::GetInstance()->MapObjectToSpan函数获取对应的Span。
  6. 将当前内存块加入到Span的空闲内存块链表中,并更新Span的使用计数和空闲内存块链表头指针。
  7. 如果Span的使用计数变为0,说明所有切分出去的小块内存都回来了,可以将该Span再次回收给PageCache。
  8. 在释放Span给PageCache之前,先解锁SpanList,然后加锁PageCache的锁,以确保线程安全。
  9. 释放Span给PageCache后,重新加锁SpanList,准备继续处理下一个内存块。
  10. 更新start为下一个内存块的地址,继续循环直到所有内存块都处理完毕。
  11. 最后,解锁SpanList,函数执行完毕。

这段代码的作用是将一组内存块释放到对应的Span中,并在Span的使用计数变为0时将Span回收给PageCache。

void CentralCache::ReleaseListToSpans(void* start, size_t size)
{size_t index = SizeClass::Index(size);_spanLists[index]._mtx.lock();while (start){void* next = NextObj(start);Span* span = PageCache::GetInstance()->MapObjectToSpan(start);NextObj(start) = span->_freeList;span->_freeList = start;span->_useCount--;// 说明span的切分出去的所有小块内存都回来了// 这个span就可以再回收给page cache,pagecache可以再尝试去做前后页的合并if (span->_useCount == 0){_spanLists[index].Erase(span);span->_freeList = nullptr;span->_next = nullptr;span->_prev = nullptr;// 释放span给page cache时,使用page cache的锁就可以了// 这时把桶锁解掉_spanLists[index]._mtx.unlock();PageCache::GetInstance()->_pageMtx.lock();PageCache::GetInstance()->ReleaseSpanToPageCache(span);PageCache::GetInstance()->_pageMtx.unlock();_spanLists[index]._mtx.lock();}start = next;}_spanLists[index]._mtx.unlock();
}
Span* PageCache::MapObjectToSpan(void* obj)
{PAGE_ID id = ((PAGE_ID)obj >> PAGE_SHIFT);auto ret = _idSpanMap.find(id);if (ret != _idSpanMap.end()){return ret->second;}else{assert(false);return nullptr;}
}

释放span到pagecache

这段代码是一个函数PageCache::ReleaseSpanToPageCache的实现,用于将Span回收给PageCache,并尝试进行前后页的合并,以缓解内存碎片问题。

函数的输入参数是一个指向Span对象的指针span

函数的主要逻辑如下:

  1. 使用一个循环,不断尝试合并Span的前面的页,直到不满足合并条件为止。
  2. 在循环中,首先计算出前一个页的页号prevId,然后在_idSpanMap中查找前一个页的Span。
  3. 如果前一个页的Span不存在,则跳出循环。
  4. 如果前一个页的Span正在使用中,则跳出循环。
  5. 如果合并后的Span的页数超过了NPAGES-1,则跳出循环。
  6. 更新合并后的Span的页号和页数,并从SpanList中移除前一个页的Span。
  7. 删除前一个页的Span。
  8. 继续循环,尝试合并Span的后面的页,直到不满足合并条件为止。
  9. 在循环中,首先计算出后一个页的页号nextId,然后在_idSpanMap中查找后一个页的Span。
  10. 如果后一个页的Span不存在,则跳出循环。
  11. 如果后一个页的Span正在使用中,则跳出循环。
  12. 如果合并后的Span的页数超过了NPAGES-1,则跳出循环。
  13. 更新合并后的Span的页数,并从SpanList中移除后一个页的Span。
  14. 删除后一个页的Span。
  15. 将合并后的Span插入到对应的SpanList的头部。
  16. 设置合并后的Span的使用标志为false。
  17. 更新_idSpanMap,将合并后的Span的页号和页号+页数-1都映射到该Span。
  18. 函数执行完毕。

这段代码的作用是将Span回收给PageCache,并尝试进行前后页的合并,以缓解内存碎片问题。合并的条件包括前后页存在且未使用,并且合并后的Span的页数不超过NPAGES-1。

void PageCache::ReleaseSpanToPageCache(Span* span)
{// 对span前后的页,尝试进行合并,缓解内存碎片问题while (1){PAGE_ID prevId = span->_pageId - 1;auto ret = _idSpanMap.find(prevId);// 前面的页号没有,不合并了if (ret == _idSpanMap.end()){break;}// 前面相邻页的span在使用,不合并了Span* prevSpan = ret->second;if (prevSpan->_isUse == true){break;}// 合并出超过128页的span没办法管理,不合并了if (prevSpan->_n + span->_n > NPAGES - 1){break;}span->_pageId = prevSpan->_pageId;span->_n += prevSpan->_n;_spanLists[prevSpan->_n].Erase(prevSpan);delete prevSpan;}// 向后合并while (1){PAGE_ID nextId = span->_pageId + span->_n;auto ret = _idSpanMap.find(nextId);if (ret == _idSpanMap.end()){break;}Span* nextSpan = ret->second;if (nextSpan->_isUse == true){break;}if (nextSpan->_n + span->_n > NPAGES - 1){break;}span->_n += nextSpan->_n;_spanLists[nextSpan->_n].Erase(nextSpan);delete nextSpan;}_spanLists[span->_n].PushFront(span);span->_isUse = false;_idSpanMap[span->_pageId] = span;_idSpanMap[span->_pageId + span->_n - 1] = span;
}

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

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

相关文章

软件测试技术之单元测试—工程师 Style 的测试方法(2)

怎么写单元测试&#xff1f; JUnit 简介 基本上每种语言和框架都有不错的单元测试框架和工具&#xff0c;例如 Java 的 JUnit、Scala 的 ScalaTest、Python的 unittest、JavaScript 的 Jest 等。上面的例子都是基于 JUnit 的&#xff0c;我们下面就简单介绍下 JUnit。 JUnit…

光模块兼容性:确保通信系统的互操作性和可靠性

在现代通信系统中&#xff0c;光模块作为重要的传输组件&#xff0c;其兼容性对于确保通信系统的互操作性和可靠性至关重要。光模块的兼容性涉及到多个方面&#xff0c;包括物理接口、协议和性能等。本文将探讨光模块兼容性的重要性以及如何确保光模块在通信系统中的良好兼容性…

深入探讨空间复杂度:前端开发中的关键概念

在前端开发中&#xff0c;我们通常关注时间复杂度和空间复杂度这两个算法概念&#xff0c;用于衡量算法或代码的性能。本文将深入介绍空间复杂度&#xff0c;探讨其在前端开发中的应用&#xff0c;并提供易懂的代码示例。 什么是空间复杂度&#xff1f; 空间复杂度是指算法在…

计算机视觉之三维重建(三)(单视图测量)

2D变换 等距变换 旋转平移保留形状、面积通常描述刚性物体运动 相似变换 在等距变换的基础增加缩放特点 射影变换 共线性、四共线点的交比保持不变 仿射变换 面积比值、平行关系等不变仿射变换是特殊的射影变换 影消点与影消线 2D无穷远点 两直线的交点可由两直线的…

Java 注解计算12生肖,java Data中获取年,根据生日日期获取生肖注解,根据输入时间获取生肖,自定义注解的方式获取生肖 根据年份时间获取十二生肖

最近&#xff0c;开发中需要增加生肖&#xff0c;但是不想增加字段&#xff0c;于是通过注解的方式&#xff0c;实现生日与生肖的转换。 话不多说&#xff0c;直接上代码&#xff0c;如下&#xff1a; 实体类中的字段&#xff0c;添加自定义注解&#xff08;ToChineseZodiacSe…

考研C语言进阶题库——更新51-60题

目录 51.银行系中有很多恒星&#xff0c;H 君晚上无聊&#xff0c;便爬上房顶数星星&#xff0c;H 君将整个银河系看做一个平面&#xff0c;左上角为原点&#xff08;坐标为&#xff08;1, 1&#xff09;&#xff09;。现在有 n 颗星星&#xff0c;他给每颗星星都标上坐标&…

使用 OpenCV Python 实现自动图像注释工具的详细步骤--附完整源码

注释是深度学习项目中最关键的部分。它是模型学习效果的决定因素。然而,这是非常乏味且耗时的。一种解决方案是使用自动图像注释工具,这大大缩短了时间。 本文是pyOpenAnnotate系列的一部分,其中包括以下内容。 1、使用 OpenCV 进行图像注释的路线图。 2、pyOpenAnnotate工…

常见前端面试之VUE面试题汇总二

4. slot 是什么&#xff1f;有什么作用&#xff1f;原理是什么&#xff1f; slot 又名插槽&#xff0c;是 Vue 的内容分发机制&#xff0c;组件内部的模板引擎使用 slot 元素作为承载分发内容的出口。插槽 slot 是子组件的一个模板 标签元素&#xff0c;而这一个标签元素是否显…

Electron学习2 使用Electron-vue和Vuetify UI库

Electron学习2 使用Electron-vue和Vuetify UI库 一、Electron-vue简介二、安装yarn三、创建Electron-vue项目1. 关于 electron-builder2. 安装脚手架3. 运行4. 打包应用程序 四、background.js说明1. 引入模块和依赖&#xff1a;2. 注册协议&#xff1a;3. 创建窗口函数&#x…

Mysql group by使用示例

文章目录 1. groupby时不能查询*2. 查询出的列必须在group by的条件列中3. group by多个字段&#xff0c;这些字段都有索引也会索引失效&#xff0c;只有group by单个字段索引才能起作用4. having条件必须跟group by相关联5. 用group by做去重6. 使用聚合函数做数量统计7. havi…

thinkphp开发的在线学习培训考试模拟考试做题练习系统带商城功能证书管理课程系统

thinkphp开发的在线学习培训考试模拟考试做题练习系统带商城功能证书管理课程系统 1、做题界面 2、前端UI的展示 3、带商城购物功能

【Redis从头学-8】Redis中的ZSet数据类型实战场景之用户积分榜

&#x1f9d1;‍&#x1f4bb;作者名称&#xff1a;DaenCode &#x1f3a4;作者简介&#xff1a;啥技术都喜欢捣鼓捣鼓&#xff0c;喜欢分享技术、经验、生活。 &#x1f60e;人生感悟&#xff1a;尝尽人生百味&#xff0c;方知世间冷暖。 &#x1f4d6;所属专栏&#xff1a;Re…

大数据 算法

什么是大数据 大数据是指数据量巨大、类型繁多、处理速度快的数据集合。这些数据集合通常包括结构化数据&#xff08;如数据库中的表格数据&#xff09;、半结构化数据&#xff08;如XML文件&#xff09;和非结构化数据&#xff08;如文本、音频和视频文件&#xff09;。大数据…

Hadoop小结(上)

最近在学大模型的分布式训练和存储&#xff0c;自己的分布式相关基础比较薄弱&#xff0c;基于深度学习的一切架构皆来源于传统&#xff0c;我总结了之前大数据的分布式解决方案即Hadoop&#xff1a; Why Hadoop Hadoop 的作用非常简单&#xff0c;就是在多计算机集群环境中营…

页面禁用鼠标右键,禁用F12打开开发者工具!!!

文章目录 问题分析方法一方法二方法二问题 今天在浏览博主文章时发现无法复制页面上的内容,也无法F12打开开发者工具,更用不了鼠标右键,于是上网找了原因并亲测可用 分析 方法一 将 <body> 改成 <body oncontextmenu=self.event.returnValue=false>方法二 …

【面试经典150题】移除元素·JavaScript版

题目来源 大致思路&#xff1a;遍历数组&#xff0c;如果遇到值为val的元素&#xff0c;使用数组最后一个元素替换它。详细过程&#xff1a; /*** param {number[]} nums* param {number} val* return {number}*/ var removeElement function(nums, val) {let i0,nnums.leng…

链表的顶级理解

目录 1.链表的概念及结构 2.链表的分类 单向或者双向 带头或者不带头 循环或者非循环 3.无头单向非循环链表的实现 3.1创建单链表 3.2遍历链表 3.3得到单链表的长度 3.4查找是否包含关键字 3.5头插法 3.6尾插法 3.7任意位置插入 3.8删除第一次出现关键字为key的节点 …

R包开发一:R与Git版本控制

目录 1.安装Git 2-配置Git&#xff08;只需配置一次&#xff09; 3-用SSH连接GitHub(只需配置一次) 4-创建Github远程仓库 5-克隆仓库到本地 目标&#xff1a;创建的R包&#xff0c;包含Git版本控制&#xff0c;并且能在远程Github仓库同步&#xff0c;相当于发布在Github。…

详解C#-static void Main(string[] args)

目录 简介: 举例: 输出结果:​ 总结&#xff1a; 简介: 在C#中static void Main(string[] args)这个句话有什么作用&#xff0c;分别代表什么意思&#xff01;&#xff01; 这句话是入口函数的声明&#xff0c;指定了C#程序的入口点&#xff0c;并定义了一个名为”Main”静…

存储系统性能优化中IOMMU的作用是什么?

一、IOMMU原理 IOMMU(Input/Output Memory Management Unit)是一种用于管理计算机内存的技术,它允许将物理内存映射到虚拟地址空间。IOMMU通过使用专用的硬件来管理和优化内存访问,从而提高系统性能和稳定性。本文将详细介绍IOMMU的原理,并介绍一些应用案例和典型的问题解…