【tcmalloc】(四)centralcache设计(申请)

一.整体框架

中心cache,用于threadcache的桶节点没有自由链表时,向下寻找内存的下一层数据结构。起到均衡调度的作用。同样采取和线程缓存一样的哈希桶映射规则。这样方便能直接找到一下层的桶节点,不同点是中央内存区是所有线程共享的所以就有加锁问题。这里用的是桶锁。这样的优点是不同桶之间不会有竞争问题。不同线程申请不同大小的内存空间,会去不同的桶低下找空间。

另一个不同点,线程内存区下挂的是大当前桶节点,同样大小的小内存。中央内存区挂的是span,是以页为单位的大块内存,同时每块span都被切分成了线程内存区对应的内存小块。这样就可以直接拿给线程内存区用。中央内存区挂的是由span构成的自由链表。

不同桶节点下的span大小是不一样的。跟线程内存区桶越靠后申请的内存越大一样,中央内存区桶越靠后span越大。 可以通过一个变量来控制当前节点每个span块 的页大小。同时因为每次拿的线程小内存块是固定的,span本身大小可以确定,就能计算出一共能拿出多少块当前桶节点对应的小内存。可以用来记录当前span块的使用情况。 可以用来处理向下一层传递合并页,同时处理内存碎片问题,这是pagecache的功能。

centralcache主要是承上启下

二.核心细节

中央内存区的桶节点是可以有多个span的同时每个span剩余的小块内存数量是不确定的,因为申请和释放可能同时进行。可能会有线程释放内存回到中央内存区。

同时这里的自由链表是带头双向的,因为不确定哪个位置的span可能要被释放回下一个层次,pagecache。双向链表删除更好操作一些。

因为所有线程公用中央内存区,所以应该将中央内存区设计成单例模式。

三.截取桶节点一段自由链表

获取当前自己桶节点下的一段链表给线程内存区

size_t CentralCache::FetchRangeObj(void*& start, void*& end, size_t fetchNum, size_t size )//链表区间的头尾指针,获取个数和获取大小
{
	size_t index = SizeRules::Index(size);		
	_spanList[index].Getmtx().lock();//多线程下访问同一桶有竞争问题
	
	Span* span = GetOneSpan(_spanList[index],size);  //获取一个非空桶块,得先找到一个有自由链表的桶块
	assert(span);
	assert(span->_freeList);
							// 从span中获取fetchNum个对象
							// 如果不够fetchNum个,有多少拿多少
	start = span->_freeList;//让范围指针取到新的内存块范围
	end = start;
	size_t i = 0;
	size_t actulNum = 1;	//实际取到的数量默认就有一块
														//遍历想要的范围,同时防止span内的内存块不够
	//两种遍历方法可以创建一个新变量cur去跑直到为空并且拿到了足够的小内存块,不过这种方法不好因为我们需要断开自由链表的链接,这样找不到之前的指针
	while (i < fetchNum - 1 && NextObj(end) != nullptr)//指针少走一次这样左右指针都可取,有利于断开
	{
		end = NextObj(end);
		i++;
		actulNum++;
	}
									//将这段内存块从span里拿出来
	span->_freeList = NextObj(end);//span指向end后面剩下的内存块
	NextObj(end) = nullptr;			//end和后面的内存块断开
	span->_useCount += actulNum;

	
	_spanList[index].Getmtx().unlock();
	
	return actulNum;
}

四.找一个不是空的桶(和pagecache联动)

主要负责去下一层拿一个桶节点上来后将桶节点切分成小块内存挂在当前位置的自由链表上

关于centralcache 和pagecache联动是桶锁和整体锁切换的问题。最好把桶锁解开在加上整体锁,在申请的情况下没什么影响,就算桶锁解开不同线程访问到了同一个桶但是因为pagechache有锁所以也会阻塞住。但是释放的时候如果不解开桶锁就没有办法合并自由链表向span里还内存

//获取一个非空span
Span* CentralCache:: GetOneSpan(SpanList &list,size_t byte_size)
{
	//寻找当前桶节点内部是否还有没使用的span内存块
	Span* it = list.Begin(); 
	//遍历当前桶节点下的挂着的所有内存块
	int i = 0;
	while (it != list.End())
	{
		i++;
		if (it->_freeList != nullptr)  //若有span未被使用则直接返回
		{
			return it;
		}
		else
		{
			it = it->_next;
		}
	}

	//走到这里说明当前位置已经没用span内存了需要去申请span(去更大块的span去切)
	// 先把central cache的桶锁解掉,这样如果其他线程释放内存对象回来,不会阻塞

	list.Getmtx().unlock();
	//进入pagecache获取页块,需要进行加锁
	PageCache::GetInstance()->Getmtx().lock();
	Span* span = PageCache::GetInstance()->NewSpan(SizeRules::NumMovePage(byte_size));	//获取新的span块
	span->_isUse = true;			//该内存块即将被使用。
	span->_objSize = byte_size;
	PageCache::GetInstance()->Getmtx().unlock();

	// 对获取span进行切分,不需要加锁,因为这会其他线程访问不到这个span,拿出来还没有连接上,其他线程看不见
	//需要计算出当前申请到的span的物理地址和内存大小(通过页号计算)

	char* start = (char*)(span->_pageid << PAGE_SHIFT);  //页号转换为物理地址
	size_t bytes = span->_n << PAGE_SHIFT;	//当前span的字节数
	char* end = start + bytes;

	//将当前span切割成自由链表并悬挂进桶节点
	// 1、先切一块下来去做头节点,方便尾插(尾插好一些,能保证物理地址连续,缓存利用率高)

	span->_freeList = start;  //给自由链表初始化起始地址
	start += byte_size;		//找到自由链表下一个起始位置
	void* next = span->_freeList; //用于存放下一个自由链表的地址
	while (start < end)
	{
		NextObj(next) = start; //将下一个位置地址保存在当前位置(相当于链接指针)
		next = start;			//寻找下一个位置重复这个流程(相当于指针后移)
		start += byte_size;		//物理上是连续的,逻辑上是链表
	}

	NextObj(next) = nullptr;


	//最后将切割好的内存挂给桶节点,要加锁
	list.Getmtx().lock();
	list.PushFront(span);
	//在这一层不用解锁,在外层函数中已经包含解锁
	return span;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
TCMalloc是一个由Google开发的内存管理器,被广泛用于大规模的分布式系统中。它为多线程环境下的内存分和释放提供了高效的性能。 TCMalloc的实现主要包含以下几个关键特性: 1. Thread-Caching TCMalloc使用了线程本地缓存(Thread-Caching)的机制,每个线程都维护了一个本地缓存,用于快速分配和释放内存。这样可以避免不同线程之间频繁访问共享数据结构的开销,提高了内存分配和释放的性能。 2. Central Cache TCMalloc还维护了一个全局的中央缓存(Central Cache),用于存储较大的内存块。当本地缓存不足以满足内存需求时,线程会向中央缓存申请内存。 3. Page Heap TCMalloc使用了Page Heap来管理内存。Page Heap将内存按页进行划分,并使用位图来跟踪每个页的使用情况。这样可以提高内存分配和释放的效率,并减少内存碎片化。 4. Size Class TCMalloc将需要分配的内存按照大小进行分类,每个大小类都有一个对应的内存池。这样可以提高内存分配和释放的效率,并减少内存碎片化。 5. 高效的内存分配和释放算法 TCMalloc使用了一些高效的算法来管理内存,如位图、堆栈缓存等。这些算法能够快速地分配和释放内存,并且减少内存碎片化。 总的来说,TCMalloc通过使用线程本地缓存、中央缓存、Page Heap以及高效的内存分配和释放算法,实现了高性能的内存管理。它在Google的大规模分布式系统中得到了广泛应用,并取得了很好的效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值