高并发内存池(回收)[4]

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中。

函数的输入参数是startsize,分别表示要释放的内存块的起始地址和大小。

函数的主要逻辑如下:

  1. 根据内存块的大小size计算出对应的SizeClass的索引index
  2. 获取对应索引的SpanList,并对其进行加锁。
  3. 进入一个循环,遍历要释放的内存块链表,直到遍历完所有内存块。
  4. 在循环中,首先获取当前内存块的下一个内存块地址next
  5. 根据当前内存块的地址start,通过PageCache::GetInstance()->MapObjectToSpan函数获取对应的Span。
  6. 将当前内存块加入到Span的空闲内存块链表中,并更新Span的使用计数和空闲内存块链表头指针。
  7. 如果Span的使用计数变为0,说明所有切分出去的小块内存都回来了,可以将该Span再次回收给PageCache。
  8. 在释放Span给PageCache之前,先解锁SpanList,然后加锁PageCache的锁,以确保线程安全。
  9. 释放Span给PageCache后,重新加锁SpanList,准备继续处理下一个内存块。
  10. 更新start为下一个内存块的地址,继续循环直到所有内存块都处理完毕。
  11. 最后,解锁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

函数的主要逻辑如下:

  1. 使用一个循环,不断尝试合并Span的前面的页,直到不满足合并条件为止。
  2. 在循环中,首先计算出前一个页的页号prevId,然后在_idSpanMap中查找前一个页的Span。
  3. 如果前一个页的Span不存在,则跳出循环。
  4. 如果前一个页的Span正在使用中,则跳出循环。
  5. 如果合并后的Span的页数超过了NPAGES-1,则跳出循环。
  6. 更新合并后的Span的页号和页数,并从SpanList中移除前一个页的Span。
  7. 删除前一个页的Span。
  8. 继续循环,尝试合并Span的后面的页,直到不满足合并条件为止。
  9. 在循环中,首先计算出后一个页的页号nextId,然后在_idSpanMap中查找后一个页的Span。
  10. 如果后一个页的Span不存在,则跳出循环。
  11. 如果后一个页的Span正在使用中,则跳出循环。
  12. 如果合并后的Span的页数超过了NPAGES-1,则跳出循环。
  13. 更新合并后的Span的页数,并从SpanList中移除后一个页的Span。
  14. 删除后一个页的Span。
  15. 将合并后的Span插入到对应的SpanList的头部。
  16. 设置合并后的Span的使用标志为false。
  17. 更新_idSpanMap,将合并后的Span的页号和页号+页数-1都映射到该Span。
  18. 函数执行完毕。

这段代码的作用是将Span回收给PageCache,并尝试进行前后页的合并,以缓解内存碎片问题。合并的条件包括前后页存在且未使用,并且合并后的Span的页数不超过NPAGES-1。

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;
}

你可能感兴趣的:(C++,c++)