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

【高并发内存池——项目】定长内存池——开胃小菜

提示:高并发内存池完整项目代码,在主页专栏项目中

文章目录

提示:高并发内存池完整项目代码,在主页专栏项目中

先设计一个定长的内存池

一、为什么需要定长内存池?

🏢 传统内存分配的痛点

🏭 内存池的解决方案

二、定长内存池核心设计思想

1. 整体架构

2. 类定义解析

三、关键技术实现深度解析

1. 内存申请策略

2. 内存分配(New方法)

3. 内存释放(Delete方法)

四、空闲链表技术的巧妙运用

1. 链表存储原理

2. 内存对齐的重要性

五、性能优势分析

与传统malloc对比


先设计一个定长的内存池

       作为程序员(C/C++)我们知道申请内存使⽤的是malloc,malloc其实就是⼀个通⽤的⼤众货,什么场景 下都可以⽤,但是什么场景下都可以⽤就意味着什么场景下都不会有很⾼的性能,下⾯我们就先来设 计⼀个定⻓内存池做个开胃菜,当然这个定⻓内存池在我们后⾯的⾼并发内存池中也是有价值的,所 以学习他⽬的有两层,先熟悉⼀下简单内存池是如何控制的,第⼆他会作为我们后⾯内存池的⼀个基础组件。

#include <iostream>
#include <vector>
#include <time.h>
using std::cout;
using std::endl;#ifdef _WIN32#include<windows.h>
#else
// 
#endif// 定长内存池
//template<size_t N>
//class ObjectPool
//{};// 直接去堆上按页申请空间
inline static void* SystemAlloc(size_t kpage)
{
#ifdef _WIN32void* ptr = VirtualAlloc(0, kpage<<13, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#else// linux下brk mmap等
#endifif (ptr == nullptr)throw std::bad_alloc();return ptr;
}template<class T>
class ObjectPool
{
public:T* New(){T* obj = nullptr;// 优先把还回来内存块对象,再次重复利用if (_freeList){void* next = *((void**)_freeList);obj = (T*)_freeList;_freeList = next;}else{// 剩余内存不够一个对象大小时,则重新开大块空间if (_remainBytes < sizeof(T)){_remainBytes = 128 * 1024;//_memory = (char*)malloc(_remainBytes);_memory = (char*)SystemAlloc(_remainBytes >> 13);if (_memory == nullptr){throw std::bad_alloc();}}obj = (T*)_memory;size_t objSize = sizeof(T) < sizeof(void*) ? sizeof(void*) : sizeof(T);_memory += objSize;_remainBytes -= objSize;}// 定位new,显示调用T的构造函数初始化new(obj)T;return obj;}void Delete(T* obj){// 显示调用析构函数清理对象obj->~T();// 头插*(void**)obj = _freeList;_freeList = obj;}private:char* _memory = nullptr; // 指向大块内存的指针size_t _remainBytes = 0; // 大块内存在切分过程中剩余字节数void* _freeList = nullptr; // 还回来过程中链接的自由链表的头指针
};

一、为什么需要定长内存池?

在C++开发中,频繁的内存分配和释放是性能瓶颈的主要来源之一。让我们先看一个现实中的比喻:

🏢 传统内存分配的痛点

想象每次需要办公桌时都现买:

  • ⏰ 时间开销大:每次都要去家具市场

  • 💰 成本高昂:中间商赚差价(内存碎片)

  • 🎯 效率低下:无法批量优化

🏭 内存池的解决方案

像大型办公室统一采购:

  • 🚀 批量获取:一次性申请大块内存

  • ⚡ 快速分配:直接从池中分配,无需系统调用

  • 🔄 重复利用:释放的内存放回池中复用

  • 📦 减少碎片:固定大小分配,无外部碎片

二、定长内存池核心设计思想

1. 整体架构

三大核心组件:

  • 🗃️ 大块内存:从系统申请的内存块

  • 🔗 空闲链表:管理已释放可重用的内存块

  • 📊 分配策略:决定如何分配新内存

2. 类定义解析

template<class T>
class ObjectPool
{
private:char* _memory = nullptr;     // 指向大块内存的指针size_t _remainBytes = 0;     // 剩余可用字节数void* _freeList = nullptr;   // 空闲链表头指针
};

三、关键技术实现深度解析

1. 内存申请策略

// 直接去堆上按页申请空间
inline static void* SystemAlloc(size_t kpage)
{
#ifdef _WIN32void* ptr = VirtualAlloc(0, kpage << 13, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#else// Linux下使用brk或mmap等系统调用
#endifif (ptr == nullptr)throw std::bad_alloc();return ptr;
}

设计要点:

  • 🖥️ 跨平台支持:Windows使用VirtualAlloc,Linux使用mmap

  • 📏 按页分配:以页面为单位(通常4KB),减少系统调用次数

  • 🚨 异常安全:分配失败抛出bad_alloc异常

2. 内存分配(New方法)

T* New()
{T* obj = nullptr;// 优先复用空闲链表中的内存块if (_freeList){void* next = *((void**)_freeList);  // 获取下一个空闲块obj = (T*)_freeList;                // 当前块作为分配对象_freeList = next;                   // 更新空闲链表头}else{// 剩余内存不足时申请新的大块内存if (_remainBytes < sizeof(T)){_remainBytes = 128 * 1024;  // 申请128KB_memory = (char*)SystemAlloc(_remainBytes >> 13); // 计算页数if (_memory == nullptr){throw std::bad_alloc();}}// 从大块内存中切分size_t objSize = sizeof(T) < sizeof(void*) ? sizeof(void*) : sizeof(T);obj = (T*)_memory;_memory += objSize;_remainBytes -= objSize;}// 定位new调用构造函数new(obj)T;return obj;
}

关键技术点:

  1. 空闲链表优先:先尝试从空闲链表获取已释放的内存

  2. 内存对齐:确保每个对象至少sizeof(void*)大小,便于链表操作

  3. 批量申请:一次性申请128KB内存,减少系统调用

  4. 定位new:在指定内存地址调用构造函数

3. 内存释放(Delete方法)

void Delete(T* obj)
{// 调用析构函数清理对象obj->~T();// 头插法将内存块加入空闲链表*(void**)obj = _freeList;_freeList = obj;
}

设计精髓:

  • 🧹 资源清理:显式调用析构函数

  • 🔗 链表管理:使用头插法将释放的内存加入空闲链表

  • ⚡ 高效复用:释放的内存立即可用于下次分配

四、空闲链表技术的巧妙运用

1. 链表存储原理

关键技巧: 利用内存块本身存储链表指针

 // 释放时:将当前内存块的前4/8字节存储下一个节点的地址
*(void**)obj = _freeList;  // 将_freeList值存入obj指向的内存
_freeList = obj;           // 更新链表头// 分配时:从链表头取出节点,并更新头指针
void* next = *((void**)_freeList);  // 读取下一个节点地址
obj = (T*)_freeList;                // 当前节点作为分配对象
_freeList = next;                   // 更新链表头

2. 内存对齐的重要性

size_t objSize = sizeof(T) < sizeof(void*) ? sizeof(void*) : sizeof(T);

为什么需要对齐?

  • 📏 最小大小:确保每个内存块至少能存储一个指针(4或8字节)

  • 🧩 地址对齐:保证指针操作的正确性

  • ⚡ 访问效率:对齐的内存访问速度更快

五、性能优势分析

与传统malloc对比

特性传统malloc定长内存池
分配速度较慢(系统调用)极快(直接操作内存)
内存碎片可能产生外部碎片无外部碎片
系统调用每次分配都可能调用批量申请,极少调用
线程安全需要加锁可设计为线程本地
适用场景通用分配固定大小对象


文章转载自:

http://ujBXkS6t.xfdkh.cn
http://6eeGHjd9.xfdkh.cn
http://5DoAo4NF.xfdkh.cn
http://cq1PdQDL.xfdkh.cn
http://E8eHiitC.xfdkh.cn
http://GVoTsf2o.xfdkh.cn
http://57ooU6R4.xfdkh.cn
http://p4SwKYQi.xfdkh.cn
http://b9hqFs9J.xfdkh.cn
http://5i7wfK8g.xfdkh.cn
http://eemJD1Q6.xfdkh.cn
http://TsJGJi7R.xfdkh.cn
http://egYDMWWD.xfdkh.cn
http://FPLHCJr5.xfdkh.cn
http://y06f97G7.xfdkh.cn
http://gq46SNzU.xfdkh.cn
http://Oajll8TI.xfdkh.cn
http://fpfMVzIk.xfdkh.cn
http://4v4QBN1i.xfdkh.cn
http://PkVjy8qY.xfdkh.cn
http://ftBkn75R.xfdkh.cn
http://mZQz3JN5.xfdkh.cn
http://E3js1YnS.xfdkh.cn
http://zbX1h2AV.xfdkh.cn
http://pQeIeaQN.xfdkh.cn
http://KtMMdZBM.xfdkh.cn
http://ayM3nNtZ.xfdkh.cn
http://Ti6PqacQ.xfdkh.cn
http://oZZjI2T6.xfdkh.cn
http://RBdVkEF9.xfdkh.cn
http://www.dtcms.com/a/384398.html

相关文章:

  • 作为注册中心zk和nacos如何选型
  • 前置配置3:nacos 配置中心
  • Linux —— 进程的程序替换[进程控制]
  • [Linux] 从YT8531SH出发看Linux网络PHY驱动
  • ArcGIS定向影像(2)——非传统影像轻量级解决方案
  • 分享机械键盘MCU解决方案
  • Unity 性能优化 之 编辑器创建资源优化(UGUI | 物理 | 动画)
  • PostgreSQL——分区表
  • Elastic APM 高级特性:分布式追踪与机器学习优化
  • Ubuntu 服务器配置转发网络访问
  • Redis 数据结构源码剖析(SDS、Dict、Skiplist、Quicklist、Ziplist)
  • C#通讯之网络通讯 TCP UDP
  • 响应时间从5ms到0.8ms:威迈斯AI+DSP协同架构的突破与工程实践
  • 《WINDOWS 环境下32位汇编语言程序设计》第16章 WinSock接口和网络编程(2)
  • 算法--插入排序
  • 领码方案|权限即数据:企业系统中的字段级访问控制架构实战(Ver=1.0)
  • 【面试场景题】支付金融系统与普通业务系统的一些技术和架构上的区别
  • 数证杯顺心借JAVA网站重构详细版(服务器取证基础考点+检材+题目+重构视频)
  • 【Unity】【Photon】Fusion2中的玩家输入系统 学习笔记
  • Vue3 + Three.js 实战:自定义 3D 模型加载与交互全流程
  • 【Leetcode hot 100】102.二叉树的层序遍历
  • [Windows] 微软 .Net 运行库离线安装包 | Microsoft .Net Packages AIO_v09.09.25
  • java通过RESTful API实现两个项目之间相互传输数据
  • C++基础(13)——list类的模拟实现
  • C#/.NET/.NET Core技术前沿周刊 | 第 54 期(2025年9.8-9.14)
  • 快速上手 Jenkins
  • 【C++】STL--List使用及其模拟实现
  • Go语言双向链表list.List详解
  • 机器学习-Boosting
  • Jenkins运维之路(Jenkins流水线改造Day02-2-容器项目)