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 cache
if (_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中。
函数的输入参数是start
和size
,分别表示要释放的内存块的起始地址和大小。
函数的主要逻辑如下:文章来源:https://uudwc.com/A/g0pxM
- 根据内存块的大小
size
计算出对应的SizeClass的索引index
。 - 获取对应索引的SpanList,并对其进行加锁。
- 进入一个循环,遍历要释放的内存块链表,直到遍历完所有内存块。
- 在循环中,首先获取当前内存块的下一个内存块地址
next
。 - 根据当前内存块的地址
start
,通过PageCache::GetInstance()->MapObjectToSpan函数获取对应的Span。 - 将当前内存块加入到Span的空闲内存块链表中,并更新Span的使用计数和空闲内存块链表头指针。
- 如果Span的使用计数变为0,说明所有切分出去的小块内存都回来了,可以将该Span再次回收给PageCache。
- 在释放Span给PageCache之前,先解锁SpanList,然后加锁PageCache的锁,以确保线程安全。
- 释放Span给PageCache后,重新加锁SpanList,准备继续处理下一个内存块。
- 更新
start
为下一个内存块的地址,继续循环直到所有内存块都处理完毕。 - 最后,解锁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
。
函数的主要逻辑如下:
- 使用一个循环,不断尝试合并Span的前面的页,直到不满足合并条件为止。
- 在循环中,首先计算出前一个页的页号
prevId
,然后在_idSpanMap
中查找前一个页的Span。 - 如果前一个页的Span不存在,则跳出循环。
- 如果前一个页的Span正在使用中,则跳出循环。
- 如果合并后的Span的页数超过了NPAGES-1,则跳出循环。
- 更新合并后的Span的页号和页数,并从SpanList中移除前一个页的Span。
- 删除前一个页的Span。
- 继续循环,尝试合并Span的后面的页,直到不满足合并条件为止。
- 在循环中,首先计算出后一个页的页号
nextId
,然后在_idSpanMap
中查找后一个页的Span。 - 如果后一个页的Span不存在,则跳出循环。
- 如果后一个页的Span正在使用中,则跳出循环。
- 如果合并后的Span的页数超过了NPAGES-1,则跳出循环。
- 更新合并后的Span的页数,并从SpanList中移除后一个页的Span。
- 删除后一个页的Span。
- 将合并后的Span插入到对应的SpanList的头部。
- 设置合并后的Span的使用标志为false。
- 更新
_idSpanMap
,将合并后的Span的页号和页号+页数-1都映射到该Span。 - 函数执行完毕。
这段代码的作用是将Span回收给PageCache,并尝试进行前后页的合并,以缓解内存碎片问题。合并的条件包括前后页存在且未使用,并且合并后的Span的页数不超过NPAGES-1。文章来源地址https://uudwc.com/A/g0pxM
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;
}