【C++项目】高并发内存池第五讲内存回收释放过程介绍

内存回收

1.ThreadCache

cpp 复制代码
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.PopRang(start, end, list.MaxSize());
	CentralCache::GetInstance()->ReleaseListToSpans(start, size);
}

当闲置的内存超过一个批量单位大小的时候就开始回收,首先要计算出要回收到哪个桶的的内存,然后逐级往上回收。

2.CentralCache

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 = PageCache::GetInstance()->MapObjectToSpan(start);
		Nextobj(start) = span->_freeList;
		span->_freeList = start;
		span->_usecount--;
		if (span->_usecount == 0)
			//说明span切分出去的内存小块都回收回来了,
			//这时这个span就可以再回收给page cache,page cache可以再尝试去做前后页的合并
		{
			_spanLists[index].Erase(span);
			span->_freeList = nullptr;
			span->_prev = nullptr;
			span->_next = nullptr;

			//释放span给page cache时,使用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();

}

CentralCache回收回来还需要做前后页的合并,合成一个大的内存块,然后继续交给PageCache处理

3.PageCache

cpp 复制代码
void PageCache::ReleaseSpanToPageCache(Span* span)
{
	//大于128页的span,直接还给堆
	if (span->_n > NPAGES -1)
	{
		void* ptr = (void*)(span->_pageId << PAGE_SHIFT);
		SystemFree(ptr);
		//delete span;
		_spanPool.Delete(span);
		return;
	}
	//对span前后的页,尝试进行合并,缓解内存碎片问题(外碎片)

	//对前后的页进行合并
	while (1)
	{
		PAGE_ID prevId = span->_pageId - 1;
		//auto ret = _idSpanMap.find(prevId);

		前面的页号没有找到,不进行合并
		//if (ret == _idSpanMap.end())
		//{
		//	break;
		//}
		
		auto ret = (Span*)_idSpanMap.get(prevId);
		if (ret == nullptr)
		{
			break;
		}
		//前面相邻页的span在使用,不进行合并
		Span* prevSpan = ret;
		if (prevSpan->_isUse == true)
		{
			break;
		}
		//合并出超过128的span没办法管理,就不能继续合并
		if (prevSpan->_n + span->_n > NPAGES - 1)
		{
			break;
		}

		//合并
		span->_pageId = prevSpan->_pageId;
		span->_n += prevSpan->_n;
		_spanList[prevSpan->_n].Erase(prevSpan);
		//delete prevSpan;
		_spanPool.Delete(prevSpan);

	}


	//向后合并
	while (1)
	{
		PAGE_ID nextId = span->_pageId + span->_n;
		/*auto ret = _idSpanMap.find(nextId);
		if (ret == _idSpanMap.end())
		{
			break;
		}*/
		auto ret = (Span*)_idSpanMap.get(nextId);
		if (ret == nullptr)
		{
			break;
		}
		Span* nextSpan = ret;
		if (nextSpan->_isUse == true)
		{
			break;
		}
		if (span->_n + nextSpan->_n > NPAGES - 1)
		{
			break;
		}
		span->_n += nextSpan->_n;

		_spanList[nextSpan->_n].Erase(nextSpan);
		//delete nextSpan;
		_spanPool.Delete(nextSpan);
	}
	_spanList[span->_n].PushFront(span);
	span->_isUse = false;
	//_idSpanMap[span->_pageId] = span;
	_idSpanMap.set(span->_pageId, span);
	//_idSpanMap[span->_pageId + span->_n - 1] = span;
	_idSpanMap.set(span->_pageId + span->_n - 1, span);
}

PageCache需要将一页一一页的小块内存何合并成一张大页的内存,来解决内存碎片问题,因为大的可以切成小的,而当申请的内存大于小块的内存碎片时,就会向堆中申请,造成内存浪费。

点赞支持~

相关推荐
梦想科研社1 分钟前
【无人机设计与控制】四旋翼无人机俯仰姿态保持模糊PID控制(带说明报告)
开发语言·算法·数学建模·matlab·无人机
风等雨归期1 分钟前
【python】【绘制小程序】动态爱心绘制
开发语言·python·小程序
千穹凌帝4 分钟前
SpinalHDL之结构(二)
开发语言·前端·fpga开发
AlexMercer10127 分钟前
【C++】二、数据类型 (同C)
c语言·开发语言·数据结构·c++·笔记·算法
friklogff8 分钟前
【无标题】云端之C#:全面解析6种云服务提供商的SDK
开发语言·flask·c#
Reese_Cool21 分钟前
【C语言二级考试】循环结构设计
android·java·c语言·开发语言
海里真的有鱼22 分钟前
Spring Boot 项目中整合 RabbitMQ,使用死信队列(Dead Letter Exchange, DLX)实现延迟队列功能
开发语言·后端·rabbitmq
zxctsclrjjjcph38 分钟前
【C语言】常见的C语言概念
c语言·开发语言
小灰灰爱代码43 分钟前
C++——求3个数中最大的数(分别考虑整数、双精度数、长整数的情况),用函数模板来实现。
开发语言·c++·算法
Eiceblue1 小时前
Python 复制Excel 中的行、列、单元格
开发语言·python·excel