【高性能内存池】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();
}
相关推荐
程序员如山石5 分钟前
Qt的互斥量用法
开发语言·qt
终末圆14 分钟前
MyBatis—Plus 快速上手【后端 22】
java·开发语言·数据库·后端·sql·spring·mybatis
fareast_mzh28 分钟前
eMule allows you to optimize your queue and sharing efficiency
java·开发语言·数据库
牵牛老人38 分钟前
Qt开发技巧(九)去掉切换按钮,直接传样式文件,字体设置,QImage超强,巧用Qt的全局对象,信号槽断连,低量数据就用sqlite
开发语言·qt·sqlite
聆听HJ1 小时前
java 解析excel (本地资源)
java·开发语言·excel
不拱地的猪1 小时前
想知道为什么有DICOM格式,YAML格式,XML格式,JSON格式吗?
xml·java·c++·opencv·json
yi碗汤园1 小时前
【一文读懂】C#如何实现通用的排序功能
开发语言·数据结构·算法·c#
peihexian1 小时前
使用rust实现rtsp码流截图
开发语言·后端·rust
AutoAutoJack1 小时前
C# 字符串(String)的应用说明一
开发语言·数据结构·算法·架构·c#
master cat1 小时前
C++中数据类型的大小
开发语言·c++