thread cache
前文主要讲解了
thread cache
的框架和freeList
的具体逻辑和实现。本篇文章主要讲解thread cache
的具体实现。
- 申请内存
- 释放内存
- 当
memory
中没有内存的时候,向central cache
申请内存- 当换回来的内存太多时,还内存给
central cache
后面的两点要在说完
central cache
之后再实现,本篇文章暂时不实现。
1 thread cache代码框架
下面是 thread cache的框架。主要有四个函数,涉及内存的申请和释放。还有一个成员变量是FreeList类型的_freeLists,大小是NFREELIST(我定义为了208)
class ThreadCache
{
public:
//申请内存
void* Allocate(size_t size);
//释放内存
void Deallocate(void* ptr, size_t size);
//从中心缓存获取内存
void* FetchFromCentralCache(size_t index, size_t size);
// 释放对象时,链表过长时,回收内存回到中心缓存
void ListTooLong(FreeList& list, size_t size);
private:
//需要一个_freeList的哈希桶
FreeList _freeLists[NFREELIST]; //static const size_t NFREELIST = 208;
};
2 申请内存
函数的声明如下,参数传递的是申请的内存大小size
。
void* Allocate(size_t size);
当传入申请的内存大小size时,根据之前文章所说,不是申请多少给多少,而是根据分配规则和对齐原则来分配。引用上一篇文章所写:
假设申请
size
字节的内存大小,首先根据size
的大小判断应该分配多少个size
,例如是size = 19字节
,应该分配24字节
。其次根据size
的大小判断size
处于哪一个对齐的区间,例如size = 19
,分配为24
, 则size < 128
,则使用8字节对齐
。最终,根据这两个情况,计算出freeList
的下标。当size = 24
(注意这里是分配为24,虽然我们申请的是19),freeList的下标为3
.
再举一个例子,假设
size = 300
,则根据分配规则,应该分配size = 304字节
,根据size
的大小判断处于哪一个对齐区间,size = 304
,则为16字节对齐
。最终计算freeList
的下标。当size = 304
时,freeList
的下标为(304 - 108) / 16 + 16 = 27
(这里的计算规则下面会讲),所以freeList的下标应该为27
.
总结为三步:
1.根据size
大小判断对齐原则(是8字节对齐,还是16字节对齐等)
2.根据对齐原则和size
大小判断系统应该分配多少字节。
3.根据系统分配的字节数判断处于freeList
的多少号下标。
具体的计算过程上篇文章有讲,十分详细。
具体代码:
void* ThreadCache::Allocate(size_t size)
{
assert(size <= MAX_BYTES);
size_t alignSize = SizeClass::RoundUp(size); //alignSzie 表示最终内存给的字节数
size_t index = SizeClass::Index(size); //index表示内存块在数组中的索引
//找到下标之后,如果_freeLists中有,就直接切出来给系统用
if (!_freeLists[index].Empty())
{
return _freeLists[index].Pop();
}
else
{
//没有内存块,就去centrleCache获取对象
return FetchFromCentralCache(index, alignSize);
//这里没有实现,暂时留着函数
}
}
3 释放内存
Deallocate
的函数声明如下:
void Deallocate(void* ptr, size_t size);
参数列表解读:
1.void* ptr:表示释放的内存的指针
2.size_t size:表示释放的内存的大小
步骤:
1.根据释放内存的大小找到应该存储在freeList
中的下标
2.将该段freeList
还回到自由链表之中,用Push
函数
具体代码的实现:
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 cache
if (_freeLists[index].Size() >= _freeLists[index].MaxSize())
{
//这里可以先暂时不管,讲解完central cache之后再讲解这里
ListTooLong(_freeLists[index], size);
}
}
注意:
这里为什么不需要像Allocate一样先计算分配规则和对齐规则?
因为这里size
是之前Allocate
计算好的,已经对齐过了。所以可以直接和index
相对应。
4 总结
本篇文章主要讲解了·thread cache·申请和释放的过程。
申请的主要过程如下:
1.根据想要申请的size
通过分配和对齐原则计算出系统最终会分配的size
.
2. 根据系统分配的size
计算出freeList
的下标index
3. 根据index
找到freeList
,将这一部分内存Pop()
出来。这就是分配的过程
释放的主要过程如下:
1. 将释放的size计算出index
2. 找到freeList[index]
,将内存还回去,push()
.