当前位置: 首页 > article >正文

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

0

内存回收

  • 1.ThreadCache
  • 2.CentralCache
  • 3.PageCache

1.ThreadCache

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

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


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

点赞支持~

请添加图片描述


http://www.kler.cn/news/108711.html

相关文章:

  • 如何利用 ChatGPT 提升编程技能
  • RabbitMQ生产者的可靠性
  • 洛谷 B2029 大象喝水 C++代码
  • pandas笔记
  • spring-aop-execution表达式
  • vue3.0运行npm run dev 报错Cannot find module node:url
  • 文心一言 VS 讯飞星火 VS chatgpt (123)-- 算法导论10.4 4题
  • Java SE 学习笔记(十七)—— 单元测试、反射
  • POJ 1201 Intervals 线段树
  • 微信小程序之投票管理
  • Leetcode—274.H指数【中等】
  • Java 四种引用类型
  • 【网络协议】聊聊TCP如何做到可靠传输的
  • redis 常用方法
  • 71 搜索二维矩阵
  • 大数据之LibrA数据库常见术语(十)
  • Springmvc 讲解(1)
  • 嵌入式开发
  • Animate(原Flash)和木疙瘩中遮罩动画秒懂
  • 黑客在Pwn2Own Toronto上以58个零日漏洞赚取超过100万美元
  • dump与strace命令实战之分析keystore死锁导致watchdog问题
  • 正向代理和反向代理
  • 基于springboot实现校园疫情防控系统项目【项目源码+论文说明】计算机毕业设计
  • 【多线程面试题 八】、说一说Java同步机制中的wait和notify
  • 如何借助数据集更好的评估NLP模型的性能?
  • 【数据结构】数组和字符串(九):稀疏矩阵的链接存储:十字链表的插入、查找、删除操作
  • 大数据可视化BI分析工具Apache Superset实现公网远程访问
  • 【数据结构】Map和Set
  • 深入浅出排序算法之基数排序
  • OS的Alarm定时器调度机制