首页 > 系统相关 >高并发内存池(五)Thread Cache、Central Cache回收功能的实现

高并发内存池(五)Thread Cache、Central Cache回收功能的实现

时间:2024-07-28 20:26:51浏览次数:9  
标签:span Thread Cache 内存 Central Span size

目录

一、Thread Cache的回收实现

1.1Thread Cache回收框架

1.2Thread Cache回收实现

二、Central Cache

2.1Central Cache回收框架

2.2Central Cache回收实现


一、Thread Cache的回收实现

1.1Thread Cache回收框架

在实现完整的高并发内存池内存分配逻辑以后,回收逻辑就变得清晰明了很多了,怎么分出去的就怎么收回来,不过相比较与分配逻辑,回收则是从小块内存合并到大块内存最后再次挂入Page Cache进行下次使用,本文博主对Thread Cache的实现进行了详细的拆分及讲解。

 因为小块内存的使用频率相比于大块内存是更多的,而Thread Cache也是直接面对用户的第一层,所以在大逻辑上,当用户要释放数据时,就先调用Thread Cache的释放函数:

这里只是先对释放的逻辑进行初步实现,针对超大内存的申请,博主会在后面对代码进行优化。

static void ConcurrentFree(void* ptr,size_t size)//释放
{
	assert(pTLSThreadCache);

	pTLSThreadCache->Deallocate(ptr,size);
}

 Thread Cache释放内存遵循以下两个特点:

1. 当释放的内存小于256KB时将内存释放回thread cache中,计算size所映射的自由链表桶组成的_freelist中的位置下标index,然后将对象Push 到_freelists[index]中。

2. 当哈希桶中某个_freelist链表的长度过长,空闲内存块个数达到阈值(一般将_MaxSize也就是每个链表中单次的最大申请数作为这个值),则回收一部分内存对象到Central Cache中。

1.2Thread Cache回收实现

void ThreadCache::Deallocate(void* ptr, size_t size)//释放空间,顺便收集内存碎片返还给central cache
{
	assert(ptr);
	assert(size <= MAX_BYTES);
	//size_t alignSize = SizeClass::RoundUp(size);
	//还回来时不需要再去做对齐,因为申请时申请到的就是对齐完毕的,释放时肯定是桶内存在的

	//去找所要释放的size所在的桶,然后将要释放的对象进行Push插入
	size_t index = SizeClass::Index(size);
	_freelist[index].Push(ptr);

	//当一个桶内链表长度大于一次批量申请的内存时就切一段list还给central cache
	if (_freelist[index].Size() >= _freelist[index].MaxSize())
	{
		ListTooLong(_freelist[index], size);
	}

}                                               
void ThreadCache::ListTooLong(FreeList& list, size_t size)
{
	void* start = nullptr;
	void* end = nullptr;
	list.ringpop(start, end, list.MaxSize());

	CentralCache::GetInstance()->ReleaseListToSpans(start, size);//将当前进程中有大量空闲内存的freelist切出一部分还给Central Cache
}

Thread Cache中的回收逻辑分为两部分:

1、将小块内存放到对应的桶中的Dellocate

2、当桶中内存块达到一定数量后将其放回Central Cache中。

二、Central Cache

2.1Central Cache回收框架

1、当Thread_cache中某个桶出现内存过长或者线程退出销毁,则会将内存释放回central cache同样的下标所对应的桶中,释放回来时通过对链表中一个个内存块所算出的页号的查找,对应到具体的Span中,并将该Span中的use_count--。

2、当use_count减到0时则表示所有对象都回到了span,这时则将span重新放回回page cache, 因为Span中存储的内存大小都是整数页,所以在page cache中会对前后相邻的空闲页进行合并。

2.2Central Cache回收实现

将来自某个Thread Cache中空闲的内存块链表接收以后,找到和其大小相对应的桶后,对其地址计算页号,找到该桶中对应的Span,然后将其头插到Span中的freelist之中,再将该Span的usecount--,然后去处理链表中的下一个内存块,直到将整个链表中的内存块都放到对应的Span当中。

当Span中的usecount为0时,就要将该Span从对应的桶中删除(并不是释放了只是从Central Cache中删除),将Span中的数据都清理干净,然后将Span再交给Page Cache的回收函数对其进行前后空闲页合并和回收。

//回收从Thread Cache传回来的内存
void CentralCache::ReleaseListToSpans(void* start, size_t size)//strat:传回来链表的起始地址
{
	size_t index = SizeClass::Index(size);
	//计算出从此处thread Cache中传回的批量内存块属于central cahce中的哪个spanlist后,
	//对其进行加锁,将内存放回去
	_spanList[index]._mtx.lock();
	while (start)
	{
		void* next = NextObj(start);

		//关于为什么要查找页号才能确认span,因为虽然我们可以确认当前size在central cache的哪个桶
		//但是每个桶中有大量的span,如果一个一个span去便利对比根据地址计算后的页号
		//有n个span,每个sapn有n个切好的小内存,复杂度直接n方了铁铁,所以直接在pagecache切分span时就建立页号和某一具体span的地址

		Span* span = PageCache::GetInstance()->MapObjectToSpan(start);//查找当前内存块所对应的页号
		NextObj(start) = span->_freeList;
		span->_freeList = start;
		span->_useCount--;//将span中记录已经使用的_usecount--

		//当这个span中的usecount=0时,说明当前span切分出去的小块内存全部回来了
		//此时就可以将当前span回收给page cache
		if (span->_useCount == 0)
		{
			_spanList[index].Erase(span);
			span->_freeList = nullptr;
			span->_next = nullptr;
			span->_prev = nullptr;
			//span->_isUse=false;不能在这里直接置为false,如果此时再有其他线程归还,前后合并时            
            //如果合并到这里的Span,就会出现大问题
			//此时这个span已经和central cache的spanlist完全断开了连接
			//直接解锁允许其他线程继续访问当前桶
			_spanList[index]._mtx.unlock();

			//去调用Page Cache的大锁
			PageCache::GetInstance()->_pageMtx.lock();
			PageCache::GetInstance()->ReleaseSpanToPageCache(span);
			PageCache::GetInstance()->_pageMtx.unlock();

			_spanList[index]._mtx.lock();//重新上锁,去处理下一个小块内存
		}
		start = next;//继续去处理下一个threadcache传回来的内存块
	}
	_spanList[index]._mtx.unlock();
}

标签:span,Thread,Cache,内存,Central,Span,size
From: https://blog.csdn.net/2201_75880188/article/details/140754347

相关文章

  • Memcached跨平台性能解码:操作系统对缓存速度的影响
    Memcached跨平台性能解码:操作系统对缓存速度的影响在分布式缓存系统的设计和部署中,Memcached因其轻量级和高性能而成为首选方案之一。然而,Memcached在不同操作系统上的性能表现可能会有显著差异。本文将深入探讨这些差异的原因,并提供实际的测试方法和代码示例,帮助系统架构......
  • iOS基础---多线程:GCD、NSThread、NSOperation
    系列文章目录iOS基础—多线程:GCD、NSThread、NSOperationiOS基础—CategoryvsExtension文章目录系列文章目录一、GCD1.GCD的任务、函数、队列a.任务b.函数c.队列2.GCD的使用a.同步函数+并发队列b.异步函数+并发队列c.同步函数+串行队列d.异步函数+串行队列e.同步函......
  • 探索Memcached的宇宙:APIs及其工作原理深度解析
    ......
  • RT-Thread多线程
    RT-Thread启动流程分析线程的状态初始状态:线程刚开始创建,还没开始运行时就处于,初始状态。就绪状态:在就绪状态下,线程按照优先级排队,等待被执行。运行状态:线程正在运行,在单核系统中,只有rt_thread_self()函数返回的线程处于运行状态,但多核系统下,运行的线程不止一个。......
  • ThreadLocal
    ThreadLocal是Java中提供的一种用于实现线程局部变量的工具类。它允许每个线程都拥有自己的独立副本,从而实现线程隔离,用于解决多线程中共享对象的线程安全问题。通常,我们会使用synchronzed关键字或者lock来控制线程对临界区资源的同步顺序,但这种加锁的方式会让未获取到锁......
  • 深度解析Memcached:内存分配算法的优化之旅
    ......
  • 计算机组成与体系结构-cache
    概念Cache,高速缓冲存储器。是解决CPU与主存之间速度不匹配而采用的一项重要技术,位于主存与CPU之间,由静态存储芯片(SRAM)组成,容量比较小,Cache存储了频繁访问的内存数据命中率CPU欲访问的信息已在Cache中的比率,设在一段程序执行期间cache完成存取次数为NC,主存完成存取次数为m,h定......
  • 连接池:Memcached的效率倍增器
    连接池:Memcached的效率倍增器在现代应用架构中,Memcached作为一项关键的缓存技术,其性能直接关系到整个系统的响应速度和扩展能力。Memcached的连接池机制是优化资源使用、提升并发访问效率的重要手段。本文将深入探讨Memcached连接池的工作原理,并展示如何通过代码实现和管理......
  • 单点故障克星:Memcached的高可用性策略
    单点故障克星:Memcached的高可用性策略在构建分布式系统时,Memcached作为一个广泛使用的高性能分布式内存缓存系统,其单点故障问题不容忽视。单点故障指的是系统中的一个组件发生故障导致整个系统或服务不可用。本文将深入探讨如何避免Memcached的单点故障问题,确保缓存服务的......
  • ScheduledThreadPoolExecutor
    定时任务ScheduledThreadPoolExecutor类有两个用途:指定时间延迟后执行任务;周期性重复执行任务。JDK1.5之前,主要使用Timer类来完成定时任务,但是Timer有以下缺陷:Timer是单线程模式;如果在执行任务期间某个TimerTask耗时较久,就会影响其它任务的调度;Timer的任务调度是基于......