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

【C++项目】从零实现高并发内存池(一):核心原理与设计思路

一:什么是内存池?

1. 池化技术

  • 定义:程序先向系统申请过量资源,自己管理备用,避免频繁申请系统资源的开销。
  • 核心思想:提前申请 → 自行管理 → 快速复用,提升运行效率。
  • 常见应用:内存池、连接池、线程池、对象池等。
  • 线程池例子
    • 预先启动若干线程,让它们休眠;
    • 收到客户端请求时,唤醒池中一个线程处理;
    • 处理完后线程回到休眠状态,等待下次复用。

2. 内存池

  • 定义:程序预先向操作系统申请一块足够大的内存,之后:
    • 申请内存:不从系统直接申请,而是从内存池获取;
    • 释放内存:不直接还给系统,而是归还到内存池;
    • 真正释放:程序退出或特定时机,才将内存还给系统。

3. 内存池主要解决的问题

  1. 效率问题
    • 避免频繁调用malloc/free(系统调用开销大);
    • 内存池内部分配/回收速度更快,提升程序性能。
  2. 内存碎片问题
    • 频繁分配/释放不同大小内存,会产生大量无法利用的小内存块(内存碎片);
    • 内存池通过合理组织内存块,减少碎片,提高内存利用率。

以下这种是外碎片的例子,还回来的时候不连续所以造成空间碎片化

还有内碎片,我们在后面会提到

维度内碎片外碎片
位置已分配内存块内部已分配内存块之间的空闲区
本质分配过剩,“用不完”空闲分散,“凑不齐”
可见性对分配者可见(占用了但没用)对分配者不可见(整体空闲但用不了)
影响单个内存块利用率低无法满足大块内存分配请求
典型解决精细化大小类、减少对齐填充页合并、伙伴系统、内存池

4. malloc 的本质

  • C/C++ 动态申请内存的接口是malloc,但它并非直接从堆获取内存
  • malloc本身就是一个内存池实现
    • 向操作系统“批发”大块内存;
    • 再将这块内存“零售”给程序使用;
    • 当内存售罄或程序有大量需求时,才向操作系统再次“进货”(系统调用)。
  • malloc 有多种实现方案,不同编译器/平台使用不同版本:
    • Windows(VS系列):微软自研的 malloc 实现。
    • Linux(GCC):glibc 中的ptmalloc(最常见的开源实现)。

二:先设计一个定长的内存池学习一下

我们知道申请内存的使用的是malloc,malloc是通用的,但是任何场景都可以就意味着任何场景都不会有一个很高的性能,现在我们先设计一个定长的内存池去熟悉一下简单内存池是怎么控制的,以及会作为我们后面的内存池的一个组件。

主要实现了一个简单的定长内存池ObjectPool,旨在通过复用内存块来减少频繁new/delete带来的系统开销

1. 核心模块解析

系统级内存分配SystemAlloc

  • 功能:封装了操作系统层面的内存申请接口。
  • 实现:在 Windows 下使用VirtualAlloc按页申请空间(kpage<<13kpage * 8KB)。
    inline static void* SystemAlloc(size_t kpage) { void* ptr = VirtualAlloc(0, kpage << 13, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (ptr == nullptr) throw std::bad_alloc(); return ptr; }

内存池类ObjectPool

这是一个模板类,用于管理特定类型T的对象内存。

  • 成员变量
    • _memory:指向当前正在使用的大块内存块的指针。
    • _remainBytes:记录当前大块内存剩余可用字节数 。
    • _freelist:自由链表头指针,指向已归还且可复用的对象内存。
  • 核心方法New
    1. 优先复用:检查_freelist是否为空。若不空,从链表头部取出一个节点返回。
    2. 内存不足时扩容:若_remainBtyes不足,则通过向堆申请新的 128KB 内存块。
    3. 切割内存:计算对象大小(考虑指针大小对齐),移动_memory指针,减少_remainBtyes
    4. 构造对象:使用new (obj) T在指定内存地址调用构造函数。
  • 核心方法Delete
    1. 析构对象:显式调用析构函数obj->~T()
    2. 归还内存:将对象指针通过头插法插入_freelist链表,供下次New复用。

测试结构TreeNode与测试函数TestObjectPool

  • TreeNode:一个简单的二叉树节点结构,用于作为内存池管理的对象类型。
  • TestObjectPool
    • 对比逻辑:分别使用原生new/deleteObjectPoolNew/Delete进行多轮次(Rounds)、大数量(N)的申请释放操作。
    • 计时:使用clock()记录两种方式消耗的 CPU 时钟 ticks。
    • 预期结果:通常情况下,ObjectPool耗时会显著低于原生new/delete,因为减少了系统调用和内存碎片。
    • 结果:成功达成提高性能效果,实现的内存池相比直接new分配内存,性能提升了约 2.26 倍(111 / 49 ≈ 2.26),符合对象池技术的预期效果。

2. 关键总结

  • 自由链表(Free List):通过_freelist将释放的对象内存串联起来。Delete时将对象转为链表节点(利用对象内存本身存储 next 指针),New时直接从链表头部摘取。
  • 内存块管理:不是一次性申请所有内存,而是当当前内存块(128KB)用完时,再申请新的块。
  • Placement New:使用new (obj) T语法,在已分配的原始内存上构造对象,分离了内存分配与对象构造。

三:高并发内存池整体框架设计

1.设计背景

现代开发多为多核多线程环境,内存申请场景存在激烈锁竞争;常规malloc在高并发场景下性能不足,因此需设计高并发内存池(模仿tcmalloc),核心解决三大问题:

  1. 性能问题;
  2. 多线程环境锁竞争问题;
  3. 内存碎片问题。

二、整体架构(三层结构)

cmalloc采用thread cache + central cache + page cache三层架构,实现高并发下的高效内存管理。

1. thread cache(线程缓存)

这个的特点就是要的字节比较少的时候也会多给,

  • 属性每个线程独有,线程本地缓存,无锁竞争。
  • 适用场景:分配小于256KB的小块内存。
  • 核心优势:线程从自身thread cache申请内存时无需加锁,是高并发的核心支撑点。
  • 运行逻辑:thread cache按需从central cache获取内存对象;线程释放内存时,先放回自身thread cache(暂不立即归还central cache)。

2. central cache(中心缓存)

  • 属性所有线程共享的中心缓存层。
  • 核心作用
    • 统一管理内存对象的分配与回收,平衡各线程内存占用;
    • 避免单个线程长期占用过多内存,导致其他线程内存紧张,实现内存按需调度、均衡分配
  • 锁机制:存在锁竞争,但竞争程度低(原因:仅thread cache内存不足时才会向其申请,且采用桶机制细化锁粒度)。
  • 运行逻辑:thread cache内存耗尽时,向central cache申请批量内存对象;thread cache释放内存时,也归还给central cache统一管理。

3. page cache(页缓存)

  • 属性:位于central cache上层的缓存层,以页为单位管理内存。
  • 核心作用
    • 为central cache提供大块内存支撑:central cache内存不足时,从page cache分配连续页,切割为固定大小小块内存供central cache使用;
    • 解决内存碎片:当某个span的多个跨度页对应的对象全部回收后,page cache会回收这些满足条件的span,合并相邻的页,组成更大的连续页,缓解内存碎片。
  • 运行逻辑
    1. 向系统申请大块内存(页)时,优先从page cache获取;
    2. 回收central cache的内存时,先归还给page cache,再由page cache判断是否合并页、归还系统。

三、核心设计逻辑总结

层级

核心特性

解决问题

关键机制

thread cache

线程独享、无锁、小内存分配

高并发下的锁竞争、性能瓶颈

线程本地缓存,批量申请/释放

central cache

全局共享、锁竞争弱、均衡调度

多线程内存分配不均、锁竞争激烈

桶机制+按需分配,减少锁粒度

page cache

页级管理、合并相邻页

内存碎片、大块内存分配

页级缓存+内存合并,缓解碎片

四、核心价值

通过三层分级缓存,将内存分配的锁竞争从“全局”下沉到“线程本地”,大幅提升多线程并发分配效率;同时通过内存合并机制,有效降低内存碎片,兼顾性能与内存利用率。

http://www.jsqmd.com/news/501658/

相关文章:

  • 在 SAP BTP 上选对开发武器:从 No/Low Code 到 Pro-Code 的项目落地指南
  • 079基于spring+boot的快递分拣管理系统-springboot+vue
  • COMSOL激光淬火相变模拟
  • 基于跨模态医学图像生成模型的早期肺癌风险评估研究-(resnet)-大数据深度学习算法毕设毕业设计项目-含完整论文源码
  • 2389168‑47‑0,18:1 PE-PEG2000-benzylguanine:脂质体靶向递送 SNAP‑抗体偶联专用辅料
  • 2026年低噪音运行风机排名,哪个品牌性价比高 - myqiye
  • 永磁同步电机的无传感器控制就像给电机装上隐形的眼睛,不需要物理编码器就能感知转子位置。今天咱们聊聊全速域控制的实战经验,重点看看不同转速区间怎么玩转算法
  • linux的指令(2)
  • 【力扣-239. 滑动窗口最大值[特殊字符]】Python笔记
  • 为内容账号寻找动漫头像,2026年我的素材站点筛选流程
  • 标准值与非标准值校准
  • C语言从入门到进阶——第15讲:深入理解指针(5)
  • day14还是先学ssm吧
  • 和我一起学软件架构:C编译流程
  • DC-DC锂电池充电电源电路设计。 包含锂电池充电电路,升压电路,电压均衡电路等电路组成
  • 软件测试入门封神指南!从理论到实战,核心知识点一篇全覆盖
  • 说说背胶绿色的一线品牌有哪些,广州地区好用又靠谱的推荐 - 工业设备
  • Phi-3 Forest Laboratory 工具链整合:Visual Studio Code高效开发插件推荐与配置
  • CPT306 Principles of Computer Games Design 电脑游戏设计原理 Pt.2 游戏引擎
  • LLM Agent 非法动作频发?Google DeepMind 用 AutoHarness 自动生成代码“安全带”,小模型直接反超大模型!
  • 工具管理化技术工具选型与集成评估
  • 2026年4月初级药师短期冲刺备考工具测评:精准押题才是提分关键! - 孤篇横绝
  • 嵌入式工程师必学(176):深入ADC
  • 协程学习笔记2
  • 手把手教你用OMNet++和NESTING搭建TSN仿真环境(Ubuntu 16.04/18.04版)
  • 不同类型电动汽车充电负荷蒙特卡洛法模拟研究(包括常规充电、快速充电、更换电池) 运用蒙特卡洛法...
  • 分期乐额度回收常见误区,教你轻松实现快速变现 - 团团收购物卡回收
  • 2026年4月初级药师考前冲刺平台哪家靠谱?深度测评5大热门工具! - 孤篇横绝
  • 免费体验:yz-bijini-cosplay镜像,RTX 4090用户的AI绘画利器
  • 指数暴跌,散户运用量化交易拯救自己,量化交易开通