当前位置: 首页 > news >正文

项目日记---高并发内存池整体框架

内存池整体结构

首先,我们的内存池采用了三层缓存结构来实现不同的功能,每一层直接都紧密连接。

三层分别是:

  • ThreadCache(线程缓存结构)
  • CentralCache(中心缓存结构)
  • PageCache(页缓存结构)

接下里我们简单介绍一下这几个结构

ThreadCache:线程缓存是每个线程独享的缓存结构,线程对内存块的申请和释放都在这个缓存中进行,用于小于256kb的内存块的分配,由于每个线程独享一个ThreadCache结构,所以对这个结构的操作是无需加锁的。

CentralCache:中心缓存是所有线程共享的缓存结构,当ThreadCache中没有内存可供使用时,便向CentralCache申请内存,同时CentralCache会在合适的时机回收ThreadCache所申请的内存块,避免一个线程占用太多内存而其他线程无内存可用,达到内存均衡调度的目的。由于多个线程公用一个CentralCache,所以会存在竞争问题,为了避免竞争需要加锁,这里采用桶锁,即对于不同大小的内存对象的申请都有各自的桶锁,其次只用在ThreadCache中没有内存对象可以使用时才会申请,所以这里的锁竞争一般不会很激烈。

PageCache:

page cache中存储的内存是以页为单位进行存储及分配的,当central cache需要内存时,page cache会分配出一定数量的页分配给central cache,而当central cache中的内存满足一定条件时,page cache也会在合适的时机对其进行回收,并将回收的内存尽可能的进行合并,组成更大的连续内存块,缓解内存碎片的问题。

以上只是简单的介绍,并不能帮助我们完全理解这三个结构,后续我会给出详细讲解。

三层缓存的结构:

 各个部分的主要作用

        thread cache主要解决锁竞争的问题,每个线程独享自己的thread cache,当自己的thread cache中有内存时该线程不会去和其他线程进行竞争,每个线程只要在自己的thread cache申请内存就行了。

        central cache主要起到一个居中调度的作用,每个线程的thread cache需要内存时从central cache获取,而当thread cache的内存多了就会将内存还给central cache,其作用类似于一个中枢,因此取名为中心缓存。

        page cache就负责提供以页为单位的大块内存,当central cache需要内存时就会去向page cache申请,而当page cache没有内存了就会直接去找系统,也就是直接去堆上按页申请内存块。

thread cache结构: 

如图所示,threadcache是一个哈希桶的结构,每个桶后挂着的都是大小相同的内存对象,也就是每个桶都是一个自由链表。

如何申请内存?

申请内存首先要做的就是内存对齐,比如线程1申请6字节的内存,但是threadcache中并没有6字节的哈希桶,于是便按照规则进行对齐到8字节,threadcache在8字节的哈希桶中查找是否存在内存对象,存在则返回不存在则向centralcache申请。

如何释放内存?

 释放内存也很简单,对照定长内存池的自由链表,释放回来的内存对象根据大小挂到对应大小的桶的自由链表里供重复使用。如果某个桶的自由链表中内存对象足够多则会将部分内存还给centralcache。

central cache结构:

同样也是哈希桶,但是和threadcache稍有不同。每个桶后面挂的不是单个内存对象而是一个个span组成的链表,每个span按照对应桶的大小被切分为一个个小的内存对象,例如8字节桶中,每个span被切割成若干8字节的内存对象,其他桶同理。

每个span管理的都是一个以页为单位的大块内存,每个桶里面的若干span是按照双链表的形式链接起来的,并且每个span里面还有一个自由链表,这个自由链表里面挂的就是一个个切好了的内存块,根据其所在的哈希桶这些内存块被切成了对应的大小。

pagecache结构:

pagecache虽然也是哈希桶结构,但是桶的下标不同,每个下标表示桶内的span对象有多少页。

pagecache是服务于centralcache的所以这里的span并没有被切分为小块内存,当centralcache没有内存时向pagecache申请的就是某一个固定页数的span,而如何切分申请到的span由centralcache自己决定。

至于page cache当中究竟有多少个桶,这就要看你最大想挂几页的span了,这里我们就最大挂128页的span,为了让桶号与页号对应起来,我们可以将第0号桶空出来不用,因此我们需要将哈希桶的个数设置为129。

总结

这整个三层结构的设计是十分巧妙的,在线程缓存中是不需要加锁的,因为每个线程独享这个结构,这也是这个项目比较快的原因之一,在中心缓存中也不需要完全加锁,而是使用桶锁,只有当不同的线程进入到同一个桶时才会有锁互斥! 


http://www.dtcms.com/a/312613.html

相关文章:

  • 人工智能与医疗健康:AI 助力医疗的新格局
  • 信号传播速度与延时
  • [硬件电路-143]:模拟电路 - 开关电源与线性稳压电源的详细比较
  • PLC传感器接线与输出信号接线
  • Redis实战(7)-- 高级特性 Redis Stream数据结构与基础命令
  • 【0基础PS】PS工具详解--文字工具
  • .NET 开源节点编辑器,将你的程序功能模块节点化
  • pytorch 安装
  • Paxos 算法是什么?介绍 RAFT 和 ZAB,以及它们之间的区别?会有脑裂问题吗?为什么?
  • 算法竞赛阶段二-数据结构(39)数据结构栈模拟实现
  • AI陪伴赛道,热闹背后是真需求还是泡沫?
  • 应急响应整理
  • Back to the Features:附录A
  • [创业之路-532]:知识、技能、技术、科学各自解决什么问题?
  • 手机(电脑)与音响的蓝牙通信
  • 15_01_opencv_形态学滤波
  • 动态置信度调优实战:YOLOv11多目标追踪精度跃迁方案(附完整代码)
  • C++引用:高效安全的别名机制详解
  • 用于 UBI 的 Elasticsearch 插件:从搜索查询中分析用户行为
  • centos9 安装docker engine
  • Parcel 使用详解:零配置的前端打包工具
  • RPG增容3:尝试使用MVC结构搭建玩家升级UI(一)
  • Spring MVC 九大组件源码深度剖析(一):MultipartResolver - 文件上传的幕后指挥官
  • 服务端⾼并发分布式结构演进之路
  • mysql管理
  • Kafka 是什么?
  • C语言--结构体
  • Abaqus显示组怎么使用
  • 动态规划精讲:01背包问题的理论、优化与三大经典变种应用
  • kafka与其他消息队列(如 RabbitMQ, ActiveMQ)相比,有什么优缺点?