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

点赞支持~

相关推荐
leoZ23117 分钟前
AI 辅助开发的五道坎
开发语言·人工智能·视觉检测·bert·php·超分辨率重建·openvino
会周易的程序员39 分钟前
aiDgePLC — IEC61131-3 ST PLC 虚拟机调试与运行环境
c++·物联网·虚拟机·st·工业协议·iec61131·梯形图
程序员老陆39 分钟前
Qt的QThread::usleep和FFmpeg的libavutil模块的av_usleep哪个精度高一些?
开发语言·qt·ffmpeg·音视频
2601_963869951 小时前
【计算机毕业设计】基于Java的相框定制系统的设计与实现
java·开发语言·课程设计
名字还没想好☜2 小时前
Go 的 unsafe.Pointer 实战:零拷贝 []byte↔string 转换与三条铁律
开发语言·后端·golang·go·unsafe
wuminyu2 小时前
JVM利用io_uring优化堆外内存性能原理剖析
java·linux·c语言·jvm·c++
gumichef2 小时前
第二章:C/C++内存管理
c++·内存管理
天天进步20153 小时前
Python全栈项目--协同办公平台
开发语言·python
JAVA面经实录9173 小时前
集合框架 (六)
java·开发语言
xzlAwin3 小时前
Go语言多版本管理器g工具命令
开发语言·golang