一、central cache介绍
结构也是一个哈希桶,大小划分和 thread cache哈希桶一样,区别在于挂的不是自由链表而是 span 链表,里面连接了许多 span
二、span介绍
1、实现思路
span 就是 central cache 向 page cache 申请的大块内存,由一个个页(大小4KB)组成。
span 链表是一个带头双向循环链表,用于管理一个哈希桶里面所有的 span,当 thread cache 内存不够了就会拿出一个非空的 span,按照需求(thread cache 需要的小块内存大小和按照一定算法会给 thread cache 返回几个小块内存大小的内存块,如果缺一个就返回一个会导致频繁向中心缓存申请降低效率)把一个 span 分割成固定大小个数的连续内存块返回给 thread cache
2、代码实现
(1)算法确定中心缓存返回个数 static size_t NumMoveSize(size_t size)
中心思想:大块内存我给少一点,你缺一个我就最多给你两个,小块内存申请频繁就多给你几个。
(2)Span 结构体
由于 central cache 申请的 span 是从 page cache 里面申请的,所以组成单位是页,为了便于 page cache 回收内存碎片,所以要记录一个 span 的开头页号和保存的页数,还有带头双向链表的结构,为了之后回收一个完整的 span,还要实时记录 thread cache 用了多少小内存块(用了就++,用完还回来了就--,到0就代表全部回收回来了,就会在返回给 page cache 回收内存碎片),一个 span 其实是被切割成对应大小的小内存块(8,16,24 byte....)就要在自由链表管理这些内存块。
(3)Span 链表结构体
要管理好一个哈希桶对应的一个 span 链表就要有头指针,指定位置插入删除,由于去中心缓存的一个桶里面取内存有线程安全问题,所以每一个桶或者链表要有一个锁。
三、cenctral cache实现
central cache 必须只能有一个,所以是单例模式,这里我实现的是饿汉模式。定义私有静态对象 static CentralCache _sInst;
1、函数介绍
这个阶段声明三个函数(只实现两个,获取一个非空 span 涉及到 page cache)
(1)返回单例模式对象 static CentralCache* GetInstance()
return &_sInst;
(2)获取一个非空 span Span* GetOneSpan(SpanList& list, size_t size)
之后实现
(3)从中心缓存中尽量取出指定个数和大小的内存块 size_t FetchRangeObj(void*& start, void*& end, size_t fetchNum, size_t size)
先通过内存块大小 size 找到桶号
再从桶对应的 span 链表里面取出一个非空的 span
最后获取 fetchNum 个内存块,获取方法:
2、代码实现
四、thread cache向中心缓存申请内存
void* FetchFromCentralCache(size_t index, size_t size)
注意这里使用了 central cache 里面的函数,由于是单例模式,要先全局声明单例模式静态对象!!!!
1、具体实现步骤
(1)thread cache 内存块不够向 central cache 申请一个大小 size 的内存块,但是会根据慢开始反馈调节算法返回多个内存块
(2)确定具体返回几个内存块之后调用 CentralCache 里面的 FetchRangeObj 函数获取范围[start, end]的内存块,并得到实际返回的内存块个数(毕竟 span 的自由链表里面有几个内存块不确定)
(3)开始处理返回的范围[start, end]内存块
若实际返回只有一个内存块,就直接返回 start
若实际返回有多个内存块,就把范围 [start + 1, end]的内存块以自由链表形式链入 thread cache 对应桶对应的自由链表里面,最后返回 start