高并发内存池------CentralCache

        central cache也是⼀个哈希桶结构,他的哈希桶的映射关系跟threadcache是⼀样的。不同的是他的每个哈希桶位置,挂的是SpanList链表结构,不过每个映射桶下⾯的span中的⼤内存块被按映射关系切成 了⼀个个⼩内存块对象挂在span的⾃由链表中。

1.申请内存

 1. 当threadcache中没有内存时,就会批量向centralcache申请⼀些内存对象,这⾥的批量获取对象的数量使⽤了类似⽹络tcp协议拥塞控制的慢开始算法;centralcache也有⼀个哈希映射的 spanlist,spanlist中挂着span,从span中取出对象给threadcache,这个过程是需要加锁的,不 过这里的使用是⼀个桶锁,尽可能提高效率。

2. central cache映射的spanlist中所有span的都没有内存以后,则需要向pagecache申请⼀个新的 span对象,拿到span以后将span管理的内存按⼤⼩切好作为⾃由链表链接到⼀起。然后从span中 取对象给threadcache。

3. central cache中挂的span中use_count记录分配了多少个对象出去,分配⼀个对象给thread cache,就++use_coun

2.释放内存:

        当thread_cache过⻓或者线程销毁,则会将内存释放回centralcache中,释放回来时- use_count。当use_count减到0时则表⽰所有对象都回到了span,则将span释放回pagecache, pagecache中会对前后相邻的空闲⻚进⾏合并 。

3. 从中心缓存获取对象

// 从中心缓存获取对象
    void *FetchFromCentralCache(size_t index, size_t size)
    {
        // 慢开始反馈调节算法
        // 1、最开始不会一次向central cache一次批量要太多,因为要太多了可能用不完
        // 2、如果你不要这个size大小内存需求,那么batchNum就会不断增长,直到上限
        // 3、size越大,一次向central cache要的batchNum就越小
        // 4、size越小,一次向central cache要的batchNum就越大
        size_t batchNum = std::min(_freeLists[index].MaxSize(), Sizeclass::NumMoveSize(size));
        if(_freeLists[index].MaxSize() = batchNum)
        {
            _freeLists[index].MaxSize() += 1;
        }

        void* start = nullptr;
        void* end = nullptr;
        size_t actualNum = CentralCache::GetInstance()->FetchRangeObj(start, end, batchNum, size);
        assert(actualNum > 1);

        if(actualNum == 1)
        {
            assert(start == end);
            return start;
        }
        else
        {
            _freeLists[index].PushRange(NextObj(start), end);
            return start;
        }
        return nullptr;
    }
  1. 慢开始反馈调节算法: 代码注释说明了该函数使用的是一种慢开始反馈调节算法,用于优化从中心缓存获取对象的方式。

  2. 获取批量数量 (batchNum)

    size_t batchNum = std::min(_freeLists[index].MaxSize(), Sizeclass::NumMoveSize(size));

    计算应该从中心缓存获取的对象批量数量。这是通过取 _freeLists[index] 的最大大小和 Sizeclass::NumMoveSize(size) 的最小值得到的。

  3. 调整最大大小

    if(_freeLists[index].MaxSize() == batchNum)
    {
        _freeLists[index].MaxSize() += 1;
    }

    如果当前最大大小等于计算出的批量数量,则增加 _freeLists[index] 的最大大小。

  4. 初始化指针

    void* start = nullptr;
    void* end = nullptr;

    定义了两个指针 startend,初始值都为 nullptr

  5. 从中心缓存获取对象

    size_t actualNum = CentralCache::GetInstance()->FetchRangeObj(start, end, batchNum, size);

    调用 CentralCacheFetchRangeObj 方法,尝试获取指定数量的对象。actualNum 存储实际获取到的对象数量。

  6. 检查获取结果

    assert(actualNum > 1);

    断言确保至少获取到两个对象。

  7. 处理单个对象

    if(actualNum == 1)
    {
        assert(start == end);
        return start;
    }

    如果只获取到一个对象,检查 startend 是否相同,并返回 start 指向的对象。

  8. 处理多个对象

    else
    {
        _freeLists[index].PushRange(NextObj(start), end);
        return start;
    }

    如果有多个对象,将它们添加到 _freeLists[index] 列表中,并返回 start 指向的对象。

4.从中心获取一定数量的对象给ThreadCache   

 

   //从中心获取一定数量的对象给ThreadCache 
    size_t FetchRangeObj(void*& start, void*& end, size_t batchNum, size_t size)
    {
        size_t index = Sizeclass::Index(size);
        _spanLists[index]._mtx.lock();

        Span* span = GetOneSpan(_spanLists[index], size);
        assert(span);
        assert(span->_freeList);

        //从Span中获取batchnum个对象
        //如果不够那么batchnum个,有多少拿多少
        start = span->_freeList;
        end = start;
        size_t i = 0;
        size_t actualnum = 1;
        while(i < batchNum - 1 && NextObj(end) != NULL)
        {
            end = NextObj(end);
            ++i;
            ++actualnum;
        }
        span->_freeList = NextObj(end);
        NextObj(end) = nullptr;

        _spanLists[index]._mtx.unlock();

        return actualnum;
    }

        这段代码定义了一个名为 FetchRangeObj 的函数,该函数的目的是从中心缓存(CentralCache)获取一定数量的对象,并将其放入 ThreadCache函数接收四个参数:两个指针引用 startend 用于指向对象范围的起始和结束位置,batchNum 表示要获取的对象数量,size 表示对象的大小。

  1. 获取索引

    size_t index = Sizeclass::Index(size);

    使用 Sizeclass::Index 静态方法计算给定大小的对象应存放在哪个索引的 _freeLists 中。

  2. 锁定互斥锁

    _spanLists[index]._mtx.lock();

    锁定对应索引的互斥锁,以确保在操作 _freeLists[index] 时的线程安全。

  3. 获取 Span 对象

    Span* span = GetOneSpan(_spanLists[index], size);

    调用 GetOneSpan 函数从 _spanLists[index] 中获取一个 Span 对象,该 Span 包含了一定数量的连续空闲对象。

  4. 断言

    assert(span);
    assert(span->_freeList);

    确保获取到的 Span 对象不为空,并且其 _freeList 不为空。

  5. Span 中获取对象

    start = span->_freeList;
    end = start;
    size_t i = 0;
    size_t actualnum = 1;
    while(i < batchNum - 1 && NextObj(end) != NULL)
    {
       end = NextObj(end);
       ++i;
       ++actualnum;
    }

    遍历 Span 中的 _freeList,尝试获取 batchNum 个对象。如果 _freeList 中的对象数量不足 batchNum,则获取所有可用的对象。

  6. 更新 Span 状态

    span->_freeList = NextObj(end);
    NextObj(end) = nullptr;

    更新 Span_freeList 指针到 end 之后的位置,并将 end 之后的对象标记为已使用(nullptr)。

  7. 解锁互斥锁

    _spanLists[index]._mtx.unlock();

    操作完成后解锁互斥锁。

  8. 返回获取到的对象数量

    return actualnum;

    返回实际获取到的对象数量。

注意事项

  • 确保 Sizeclass::IndexGetOneSpan 函数正确实现,并且 Span 类型正确管理内存跨度。

  • 在操作 _freeLists[index] 时,需要确保互斥锁的正确使用,以避免数据竞争条件。

  • 函数中的断言用于调试目的,确保代码逻辑的正确性。在生产环境中,可能需要更复杂的错误处理机制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pipip.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值