【高性能内存池】central cache内存回收 7

thread cache中自由链表过长后,会将多出来的内存还给entral cache.thread cache还回来的内存可能不属于同一个span,因此,除了计算出要还到central cache的哪个桶之外,还需要计算出还到桶的哪个span

1. 如何根据对象的地址找到对象的页号?

页号 = 对象的地址 / 页的大小。

如果一个页的大小是100,那么第0 ~ 99都属于第0页。因为 0 ~ 99 除以100等于0.

100~199都属于第1页,因为100 ~ 199 除以100等于1.

2. 如何找到还回来的内存对应的Span?

我们可以根据还回来的内存找到对应的页号了,但是页号和Span并没有对应关系,我们无法通过页号找到对应的Span。因此,需要建立一个Span和页号的对应关系。

将这个对应关系的接口建立在page cache中。下面是page cache的结构。

cpp 复制代码
//单例模式
class PageCache
{
public:
	//获取从对象到span的映射
	Span* MapObjectToSpan(void* obj);
private:
	std::unordered_map<PAGE_ID, Span*> _idSpanMap;
};

3. 如何建立页号和Span的对应关系?

建立页号和Span的关系。

cpp 复制代码
Span* PageCache::MapObjectToSpan(void* obj)
{
	//计算页号
	PAGE_ID id = ((PAGE_ID)obj >> PAGE_SHIFT);

	/*std::unique_lock<std::mutex> lock(_pageMtx);
	auto ret = _idSpanMap.find(id);
	if (ret != _idSpanMap.end())
	{
		return ret->second;
	}
	else
	{
		assert(false);
		return nullptr;
	}*/
	//建立页号和SPan的关系
	auto ret = (Span*)_idSpanMap.get(id);
	assert(ret != nullptr);
	return ret;
}

每当page cache分配spancentral cache时,都需要记录一下页号和span之间的映射关系。此后当thread cache还对象给central cache时,才知道应该具体还给哪一个span
因此当central cache在调用NewSpan接口向page cache申请k页的span时,page cache在返回这个k页的spancentral cache之前,应该建立这k个页号与该span之间的映射关系。

因此需要在central cache向page cache申请内存时,建立映射关系。

cpp 复制代码
//获取一个k页的span
Span* PageCache::NewSpan(size_t k)
{
	assert(k > 0 && k < NPAGES);
	//先检查第k个桶里面有没有span
	if (!_spanLists[k].Empty())
	{
		Span* kSpan = _spanLists[k].PopFront();

		//建立页号与span的映射,方便central cache回收小块内存时查找对应的span
		for (PAGE_ID i = 0; i < kSpan->_n; i++)
		{
			_idSpanMap[kSpan->_pageId + i] = kSpan;
		}

		return kSpan;
	}
	//检查一下后面的桶里面有没有span,如果有可以将其进行切分
	for (size_t i = k + 1; i < NPAGES; i++)
	{
		if (!_spanLists[i].Empty())
		{
			Span* nSpan = _spanLists[i].PopFront();
			Span* kSpan = new Span;
			//在nSpan的头部切k页下来
			kSpan->_pageId = nSpan->_pageId;
			kSpan->_n = k;

			nSpan->_pageId += k;
			nSpan->_n -= k;
			//将剩下的挂到对应映射的位置
			_spanLists[nSpan->_n].PushFront(nSpan);

			//建立页号与span的映射,方便central cache回收小块内存时查找对应的span
			for (PAGE_ID i = 0; i < kSpan->_n; i++)
			{
				_idSpanMap[kSpan->_pageId + i] = kSpan;
			}

			return kSpan;
		}
	}
	//走到这里说明后面没有大页的span了,这时就向堆申请一个128页的span
	Span* bigSpan = new Span;
	void* ptr = SystemAlloc(NPAGES - 1);
	bigSpan->_pageId = (PAGE_ID)ptr >> PAGE_SHIFT;
	bigSpan->_n = NPAGES - 1;

	_spanLists[bigSpan->_n].PushFront(bigSpan);

	//尽量避免代码重复,递归调用自己
	return NewSpan(k);
}

4 central cache内存回收

4.什么时候将central cache的内存还给page cache?

thread cache还对象给central cache时,依次遍历这些对象,将这些对象插入到其对应span的自由链表当中,更新该span_usseCount计数。
thread cache还对象给central cache的过程中, 如果central cache中某个span_useCount减到0,说明这个span分配出去的对象全部都还回来了,那么此时就可以将这个span再进一步还给page cache

cpp 复制代码
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
		Span* span = PageCache::GetInstance()->MapObjectToSpan(start);
		NextObj(start) = span->_freeList;
		span->_freeList = start;
		span->_useCount--;

		// 说明span的切分出去的所有小块内存都回来了
		// 这个span就可以再回收给page cache,pagecache可以再尝试去做前后页的合并
		if (span->_useCount == 0)
		{
			//将这块Span从_spanLists中删除
			_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();
}
相关推荐
波音彬要多做11 分钟前
41 stack类与queue类
开发语言·数据结构·c++·学习·算法
捕鲸叉12 分钟前
C++软件设计模式之外观(Facade)模式
c++·设计模式·外观模式
Swift社区20 分钟前
Excel 列名称转换问题 Swift 解答
开发语言·excel·swift
一道微光23 分钟前
Mac的M2芯片运行lightgbm报错,其他python包可用,x86_x64架构运行
开发语言·python·macos
矛取矛求28 分钟前
QT的前景与互联网岗位发展
开发语言·qt
Leventure_轩先生28 分钟前
[WASAPI]从Qt MultipleMedia来看WASAPI
开发语言·qt
向宇it42 分钟前
【从零开始入门unity游戏开发之——unity篇01】unity6基础入门开篇——游戏引擎是什么、主流的游戏引擎、为什么选择Unity
开发语言·unity·c#·游戏引擎
是娜个二叉树!1 小时前
图像处理基础 | 格式转换.rgb转.jpg 灰度图 python
开发语言·python
Schwertlilien1 小时前
图像处理-Ch5-图像复原与重建
c语言·开发语言·机器学习
liuyunshengsir1 小时前
Squid代理服务器的安装使用
开发语言·php