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

高并发内存池------threadcache

一、高并发内存池整体框架设计

1.需要考虑的问题

(1)性能问题。

(2)多线程环境下,锁竞争问题。

(3)内存碎⽚问题。

2.concurrent memorypool主要由以下3个部分构成:

1. threadcache:线程缓存是每个线程独有的,⽤于⼩于256KB的内存的分配,线程从这⾥申请内存不需要加锁,每个线程独享⼀个cache,这也就是这个并发线程池⾼效的地⽅。

2. central cache:中⼼缓存是所有线程所共享,threadcache是按需从centralcache中获取的对 象。centralcache合适的时机回收threadcache中的对象,避免⼀个线程占⽤了太多的内存,⽽其 他线程的内存吃紧,达到内存分配在多个线程中更均衡的按需调度的⽬的。centralcache是存在竞 争的,所以从这⾥取内存对象是需要加锁,⾸先这⾥⽤的是桶锁,其次只有threadcache的没有内 存对象时才会找centralcache,所以这⾥竞争不会很激烈。

3. pagecache:⻚缓存是在centralcache缓存上⾯的⼀层缓存,存储的内存是以⻚为单位存储及分 配的,centralcache没有内存对象时,从pagecache分配出⼀定数量的page,并切割成定⻓⼤⼩ 的⼩块内存,分配给centralcache。当⼀个span的⼏个跨度⻚的对象都回收以后,pagecache会 回收centralcache满⾜条件的span对象,并且合并相邻的⻚,组成更⼤的⻚,缓解内存碎⽚的问题

 二、高并发内存池----threadcache

        thread cache是哈希桶结构,每个桶是⼀个按桶位置映射⼤⼩的内存块对象的⾃由链表。每个线程都会有⼀个threadcache对象,这样每个线程在这⾥获取对象和释放对象时是⽆锁的。

1.申请内存 

1. 当内存申请size<=256KB,先获取到线程本地存储的threadcache对象,计算size映射的哈希桶自由链表下标i。

2. 如果⾃由链表_freeLists[i]中有对象,则直接Pop⼀个内存对象返回。

3. 如果_freeLists[i]中没有对象时,则批量从centralcache中获取⼀定数量的对象,插⼊到⾃由链表 并返回⼀个对象。

2.释放内存

 1. 当释放内存⼩于256k时将内存释放回threadcache,计算size映射⾃由链表桶位置i,将对象Push 到_freeLists[i]。

2. 当链表的⻓度过⻓,则回收⼀部分内存对象到centralcache。

#pragma once#include"Common.h"class ThreadCache
{
public://申请和释放对象内存void* Allocate(size_t size){assert(size <= MAX_BYTES);size_t alignSize = Sizeclass::RoundUp(size);//对齐数size_t index = Sizeclass::Index(size);if(!_freeLists[index].Empty()){return _freeLists[index].Pop();}else{return FetchFromCentralCache(index, alignSize);}}void Deallocate(void* ptr, size_t size){assert(ptr);assert(size <= MAX_BYTES);//找对映射的自由链表桶,对象插入进入size_t index = Sizeclass::Index(size);_freeLists[index].Push(ptr);}//从中心缓存获取对象void* FetchFromCentralCache(size_t index, size_t size){return nullptr;}
private:FreeList _freeLists[NFREELIST];
};// TLS thread local storage
thread_local ThreadCache* pTLSThreadCache = nullptr;
//thread_local int pTLSThreadCache = 0;
#pragma once#include <iostream>
#include <cstdint>
#include <thread>
#include <vector>
#include<mutex>#include <time.h>
#include <assert.h>// ⼩于等于MAX_BYTES,就找thread cache申请
// ⼤于MAX_BYTES,就直接找page cache或者系统堆申请// thread cache 和central cache⾃由链表、哈希桶的表⼤⼩
static const size_t MAX_BYTES = 256 * 1024;
static const size_t NFREELIST = 208;#ifdef _WIN64
typedef unsigned long long PAGE_ID;
#elif _WIN32
typedef size_t PAGE_ID;
#else
// Linux
typedef uintptr_t PAGE_ID; // 使用 uintptr_t 以确保正确的大小
#endif// 管理切分好的小对象自由链表
// 获取内存对象中存储的头4 or 8字节值,即链接的下⼀个对象的地址
static void *&NextObj(void *obj)
{return *(void **)obj;
}
class FreeList
{
public:void Push(void *obj){// 头插NextObj(obj) = _freeList;_freeList = obj;}void *Pop(){assert(_freeList);// 头删void *obj = _freeList;_freeList = NextObj(obj);return obj;}bool Empty(){return _freeList == nullptr;}private:void *_freeList = nullptr;
};// 管理对象大小的对齐映射关系
class Sizeclass
{
public:// 整体控制在最多10%左右的内碎片浪费// [1,128]					8byte对齐	    freelist[0,16)// [128+1,1024]				16byte对齐	    freelist[16,72)// [1024+1,8*1024]			128byte对齐	    freelist[72,128)// [8*1024+1,64*1024]		1024byte对齐     freelist[128,184)// [64*1024+1,256*1024]		8*1024byte对齐   freelist[184,208)static size_t _RoundUp(size_t bytes, size_t alignNum){size_t alignSize;if (bytes % alignNum != 0){alignSize = (bytes / alignNum + 1) * alignNum;}else{alignSize = bytes;}return alignSize;}static inline size_t RoundUp(size_t size){if (size <= 128){return _RoundUp(size, 8);}else if (size <= 1024){return _RoundUp(size, 16);}else if (size <= 8 * 1024){return _RoundUp(size, 128);}else if (size <= 64 * 1024){return _RoundUp(size, 1024);}else if (size <= 256 * 1024){return _RoundUp(size, 8 * 1024);}else{assert(false);return -1;}}static size_t _Index(size_t bytes, size_t align_shift){return ((bytes + (1 << align_shift) - 1) >> align_shift) - 1;}// 计算到哪一个自由链表桶static inline size_t Index(size_t bytes){assert(bytes <= MAX_BYTES);// 每个区间有多少个链static int group_array[4] = {16, 56, 56, 56};if (bytes <= 128){return _Index(bytes, 3);}else if (bytes <= 1024){return _Index(bytes - 128, 4) + group_array[0];}else if (bytes <= 8 * 1024){return _Index(bytes - 1024, 7) + group_array[1] + group_array[0];}else if (bytes < 64 * 1024){return _Index(bytes - 8 * 1024, 10) + group_array[2] + group_array[1] + group_array[0];}else if (bytes < 256 * 1024){return _Index(bytes - 64 * 1024, 13) + group_array[3] + group_array[2] + group_array[1] + group_array[0];}else{assert(false);}return -1;}// 一次thread cache从中心缓存获取多少个static size_t NumMoveSize(size_t size){assert(size > 0);// [2, 512],一次批量移动多少个对象的(慢启动)上限值// 小对象一次批量上限高// 大对象一次批量上限低int num = MAX_BYTES / size;if(num < 2)num = 2;if(num > 512)num = 512;return num;}
};// 管理多个连续页大块内存跨度结构
struct Span
{PAGE_ID _pageId = 0; // 大块内存起始页的页号size_t _n = 0;       // 页的数量Span *_next = nullptr; // 双向链表的形式Span *_prev = nullptr;size_t _useCount = 0;      // 切好的小块内存,被分配给ThreadCache 计数void *_freeList = nullptr; // 切好的小块内存,自由链表
};// 带头双向循环链表
class SpanList
{
public:SpanList(){_head = new Span;_head->_next = _head;_head->_next = _head;}void Insert(Span *pos, Span *newSpan){assert(pos);assert(newSpan);Span *prev = pos->_prev;// prev newspan posprev->_next = newSpan;newSpan->_prev = prev;newSpan->_next = pos;pos->_prev = newSpan;}void Erase(Span* pos){assert(pos);assert(pos != _head);Span* prev = pos->_prev;Span* next = pos->_next;prev->_next = next;prev->_prev = prev;}
private:Span *_head;
public:std::mutex _mtx;//桶锁
};

http://www.xdnf.cn/news/4076.html

相关文章:

  • WebService的学习
  • 电子邮件相关协议介绍
  • NetSuite 2025.1 学习笔记
  • Java基础学完,继续深耕(0505)Linux 常用命令
  • TS 类class修饰符
  • 接口测试过程中常见的缺陷详解
  • Go小技巧易错点100例(三十)
  • 算法刷题篇
  • 基于Redis实现优惠券秒杀——第3期(分布式锁-Redisson)
  • UniGetUI 使用指南:轻松管理 Windows 软件(包括CUDA)
  • 【Springboot知识】Springboot计划任务Schedule详解
  • 前端懒加载(Lazy Loading)实战指南
  • 旋转图像(中等)
  • RPC是什么
  • Linux文件复制命令精要指南:cp与scp详解
  • Three.js + React 实战系列 - 客户评价区细解教程 Clients 组件✨(回答式评价 + 评分星级)
  • 51c大模型~合集124
  • TS 类型兼容性
  • 乡村饮用水厂无线网络规划与设计:融合 LoRaWAN、5G、Mesh 的分层异构方案
  • unity TMP字体使用出现乱码方框
  • 最长回文子串(动规 + 中心拓展)
  • 反转字符串2
  • 杰理-JL701-充电开机,芯片不进入休眠
  • Spring Boot 中 @Bean 注解详解:从入门到实践
  • 无人机 | 无人机设计概述
  • Springclound常用五大组件及其使用原理
  • 防止交叉验证中的数据泄露:提升模型在实际环境中的性能
  • 怎样获得真实带宽之宽带升级后
  • 014枚举之指针尺取——算法备赛
  • C++类与对象深度解析:从基础到应用