003 高并发内存池_整体框架设计

​🌈个人主页:Fan_558
🔥 系列专栏:项目一
☀ 代码仓库:高并发内存池
🌹关注我💪🏻带你学更多操作系统知识

前言

在本文中,重点是向你介绍ThreadCache、CentralCache、PageCache三个结构是怎样设计的
这三个结构对于我们代码的撰写至关重要

一、ThreadCache整体框架设计

线程缓存结构主要是解决锁竞争的问题,只要当前threadcache有空间,就不会跟其它线程竞争锁,线程缓存是每一个线程独享的结构,线程申请和释放内存都在这个缓存中进行,只能用于小于256KB的内存分配.整个线程缓存结构是不用加锁的。
定长内存池只支持固定大小内存块的申请释放,因此定长内存池中只需要一个自由链表管理释放回来的内存块。现在我们要支持申请和释放不同大小的内存块,那么我们就需要多个自由链表来管理释放回来的内存块,因此threadcache实际上一个哈希桶结构,每个桶中存放的都是一个自由链表。
在这里插入图片描述

threadcache支持小于等于256KB内存的申请,如果我们将每种字节数的内存块都用一个自由链表进行管理的话,那么此时我们就需要20多万个自由链表,光是存储这些自由链表的头指针就需要消耗大量内存,这显然是得不偿失的。
 这时我们可以选择做一些平衡的牺牲,让这些字节数按照某种规则进行对齐,例如我们让这些字节数都按照8字节进行向上对齐,那么threadcache的结构就是下面这样的,此时当线程申请1-8字节的内存时会直接给出8字节,而当线程申请9~16字节的内存时会直接给出16字节,以此类推
申请内存:

1、当内存申请size<=256KB,先获取到线程本地存储的thread cache对象,计算size映射的哈希桶自 由链表下标i。
2、如果自由链表_freeLists[i]中有对象,则直接Pop一个内存对象返回。
3、如果_freeLists[i]中没有对象时,则批量从central cache中获取一定数量的对象,插入到自由链表 并返回一个对象。

释放内存:

1、当释放内存小于256k时将内存释放回thread cache,计算size映射自由链表桶位置i,将对象Push到_freeLists[i]。
2、当链表的长度过长,则回收一部分内存对象到central cache。

二、CentralCache整体框架设计

当线程申请某一大小的内存时,如果threadcache中对应的自由链表不为空,那么直接取出一个内存块进行返回即可,但如果此时该自由链表为空,那么这时threadcache就需要向central cache申请内存了。
central cache的结构与threadcache是一样的,它们都是哈希桶的结构,并且它们遵循的对齐映射规则都是一样的,八字节为一个桶,不同的是,在CentralCache中,比如8Byte这个桶中装的是一个个Span,每个span管理的都是一个以页为单位的大块内存,每个桶里面的若干span是按照双链表的形式链接起来的,并且每个span里面还有一个自由链表,这个自由链表里面挂的就是一个个切好了的内存块,根据其所在的哈希桶这些内存块被切成了对应的大小。这样做的好处就是,当threadcache的某个桶中没有内存了,就可以直接到central cache中对应的哈希桶里去取内存就行了。

在这里插入图片描述
申请内存:

1、当thread cache中没有内存时,就会批量向central cache申请一些内存对象,这里的批量获取对象的数量使用了类似网络tcp协议拥塞控制的慢开始算法;central cache也有一个哈希映射的
2、spanlist,spanlist中挂着span,从span中取出对象给thread cache,这个过程是需要加锁的,不过这里使用的是一个桶锁,尽可能提高效率。 central cache映射的spanlist中所有span的都没有内存以后,则需要向pagecache申请一个新的 span对象,拿到span以后将span管理的内存按大小切好作为自由链表链接到一起。然后从span中取对象给thread cache。
3、centralcache的中挂的span中use_count记录分配了多少个对象出去,分配一个对象给thread cache,就++use_count

释放内存:

1、当thread_cache过长或者线程销毁,则会将内存释放回central cache中的,释放回来时-use_count。当use_count减到0时则表示所有对象都回到了span,则将span释放回page cache,pagecache中会对前后相邻的空闲页进行合并。

三、PageCache整体框架设计

页缓存是在central cache缓存上面的一层缓存,存储的内存是以页为单位存储及分配的,centralcache没有内存对象时,从page cache分配出一定数量的page,并切割成定长大小的小块内存,分配给centralcache。central cache的映射规则与thread cache保持一致,而page cache的映射规则与它们都不相同。pagecache的哈希桶映射规则采用的是直接定址法,比如1号桶挂的都是1页的span,2号桶挂的都是2页的span,以此类推

在这里插入图片描述

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

为什么这里最大挂128页的span呢?因为线程申请单个对象最大是256KB,而128页可以被切成4个256KB的对象,因此是足够的。当然,如果你想在page cache中挂更大的span也是可以的,根据具体的需求进行设置就行了

申请内存:

1、当central cache向page cache申请内存时,page cache先检查对应位置有没有span,如果没有则向更大页寻找一个span,如果找到则分裂成两个。比如:申请的是4页page,4页page后面没有挂span,则向后面寻找更大的span,假设在10页page位置找到一个span,则将10页page span分裂为一个4页pagespan和一个6页page span。
2、如果找到_spanList[1,128]都没有合适的span,则向系统使用mmap、brk或者是VirtualAlloc等方式申请128页page span挂在自由链表中,再重复1中的过程。
3、需要注意的是central cache和page cache的核心结构都是spanlist的哈希桶,但是他们是有本质 区别的,central cache中哈希桶,是按跟threadcache一样的大小对齐关系映射的,他的spanlist 中挂的span中的内存都被按映射关系切好链接成小块内存的自由链表。而pagecache中的spanlist则是按下标桶号映射的,也就是说第i号桶中挂的span都是i页内存

释放内存:

1、 如果central cache释放回一个span,则依次寻找span的前后page id的没有在使用的空闲span,看是否可以合并,如果合并继续向前寻找。这样就可以将切小的内存合并收缩成大的span,减少内存碎片。

小结

今日的分享就到这里啦,虽然没有任何的代码,但是大家一定一定要掌握好三层内存申请的结构,这对我们后面理解以及撰写代码非常重要,后面,我将会把这三层申请内存的架构再详细介绍给你

  • 14
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fan_558

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值