C++ 内存池(Memory Pool)详解
1. 基本概念
内存池是一种内存管理技术,旨在提高内存分配的效率。它通过预先分配一块大的内存区域(池),然后从中分配小块内存来满足应用程序的需求。这样可以减少频繁的内存分配和释放带来的性能开销。
2. 设计思路
内存池的设计通常遵循以下步骤:
- 预分配内存:在程序开始时,预先分配一块较大的内存区域。
- 管理空闲块:使用链表、栈或数组等数据结构管理可用内存块。
- 分配和释放:提供分配和释放接口,让用户从内存池中获取和释放内存。
- 回收机制:当内存块被释放时,将其返回到内存池中,便于后续使用。
3. 原理
内存池的核心原理是降低内存分配的时间复杂度。标准的 new
和 delete
操作在需要内存时会与操作系统频繁交互,可能会造成较大的开销。而内存池将这种频繁操作集中到池的初始化阶段,后续的分配和释放则在池内进行,速度更快。
4. 使用场景
内存池适用于以下场景:
- 游戏开发:频繁创建和销毁对象,例如子弹、敌人等。
- 高性能计算:实时系统对内存分配速度的高要求。
- 网络编程:处理大量小数据包时,内存池可以提高性能。
- 嵌入式系统:资源有限的环境中,避免频繁的动态内存分配。
5. 详细讲解
-
内存池的优势:
- 性能提升:通过减少系统调用,提高内存分配和释放的速度。
- 内存碎片减少:通过统一管理,减少内存碎片的问题。
- 简化内存管理:可以设计为自动回收机制,降低内存泄漏的风险。
-
内存池的劣势:
- 内存浪费:如果分配的块未被充分利用,可能会造成内存浪费。
- 复杂性增加:需要额外的代码管理内存池,增加了系统的复杂性。
-
扩展功能:
- 多线程支持:在多线程环境中,可以使用锁或无锁队列管理内存池。
- 调试功能:可以在分配和释放时记录堆栈信息,便于调试内存泄漏。
6. 场景示例
内存池的详细实现
1. 内存池类的结构
内存池主要由以下几个部分构成:
- 内存块:固定大小的内存单元。
- 内存池管理:负责分配和释放内存块。
- 空闲块管理:使用链表或栈来管理未使用的内存块。
2. 经典的内存池实现
#include <iostream>
#include <vector>
#include <stdexcept>
#include <cassert>
class MemoryPool {
public:
// 构造函数,初始化内存池
MemoryPool(size_t blockSize, size_t blockCount)
: m_blockSize(blockSize), m_blockCount(blockCount), m_usedBlocks(0) {
// 分配内存池
m_pool = malloc(blockSize * blockCount);
if (!m_pool) {
throw std::bad_alloc(); // 内存分配失败,抛出异常
}
// 初始化空闲块列表
for (size_t i = 0; i < blockCount; ++i) {
m_freeBlocks.push_back(static_cast<char*>(m_pool) + i * blockSize);
}
}
// 析构函数,释放内存池
~MemoryPool() {
free(m_pool);
}
// 分配内存块
void* allocate() {
// 如果没有可用的块,返回nullptr
if (m_freeBlocks.empty()) {
return nullptr; // 可以改进为扩展内存池
}
// 从空闲块列表中取出一个块
void* block = m_freeBlocks.back();
m_freeBlocks.pop_back(); // 从空闲列表中移除
m_usedBlocks++; // 增加使用计数
return block; // 返回分配的块
}
// 释放内存块
void deallocate(void* block) {
assert(block != nullptr); // 确保要释放的块不是nullptr
m_freeBlocks.push_back(static_cast<char*>(block)); // 添加到空闲块列表
m_usedBlocks--; // 减少使用计数
}
// 获取当前使用的块数
size_t usedBlocks() const {
return m_usedBlocks;
}
// 获取空闲块的数量
size_t freeBlocks() const {
return m_freeBlocks.size();
}
private:
size_t m_blockSize; // 每个内存块的大小
size_t m_blockCount; // 内存池中的块数量
void* m_pool; // 内存池的起始地址
std::vector<void*> m_freeBlocks; // 存储空闲块的列表
size_t m_usedBlocks; // 当前使用的块数
};
// 示例使用
int main() {
const size_t BLOCK_SIZE = 32; // 每个块32字节
const size_t BLOCK_COUNT = 10; // 总共10个块
MemoryPool pool(BLOCK_SIZE, BLOCK_COUNT); // 创建内存池
// 分配内存块
void* block1 = pool.allocate();
void* block2 = pool.allocate();
std::cout << "Allocated blocks: " << block1 << ", " << block2 << std::endl;
std::cout << "Used blocks: " << pool.usedBlocks() << std::endl;
std::cout << "Free blocks: " << pool.freeBlocks() << std::endl;
// 释放内存块
pool.deallocate(block1);
pool.deallocate(block2);
std::cout << "After deallocation:" << std::endl;
std::cout << "Used blocks: " << pool.usedBlocks() << std::endl;
std::cout << "Free blocks: " << pool.freeBlocks() << std::endl;
return 0;
}
3. 详细讲解
3.1 内存池的工作原理
- 初始化:在创建内存池时,预先分配一大块内存,分成多个固定大小的内存块。
- 分配:当请求内存时,从空闲块列表中取出一个块并返回。如果没有空闲块,可以考虑扩展内存池。
- 释放:释放时将内存块返回到空闲块列表中,便于后续使用。
3.2 主要功能说明
allocate()
:从空闲块中分配一个块,返回其地址;如果没有可用块,返回nullptr
。deallocate()
:将已使用的内存块返回到空闲块列表中。usedBlocks()
和freeBlocks()
:分别返回当前使用的块数和空闲块数,便于监控内存使用情况。
4. 扩展使用示例
4.1 用于游戏对象管理
假设我们有一个游戏中的子弹对象,我们可以使用内存池来管理这些对象的创建和销毁。
#include <iostream>
#include <vector>
#include <stdexcept>
#include <cassert>
class MemoryPool {
public:
// 构造函数,初始化内存池
MemoryPool(size_t blockSize, size_t blockCount)
: m_blockSize(blockSize), m_blockCount(blockCount), m_usedBlocks(0) {
// 分配内存池
m_pool = malloc(blockSize * blockCount);
if (!m_pool) {
throw std::bad_alloc(); // 内存分配失败,抛出异常
}
// 初始化空闲块列表
for (size_t i = 0; i < blockCount; ++i) {
m_freeBlocks.push_back(static_cast<char*>(m_pool) + i * blockSize);
}
}
// 析构函数,释放内存池
~MemoryPool() {
free(m_pool);
}
// 分配内存块
void* allocate() {
// 如果没有可用的块,返回nullptr
if (m_freeBlocks.empty()) {
return nullptr; // 可以改进为扩展内存池
}
// 从空闲块列表中取出一个块
void* block = m_freeBlocks.back();
m_freeBlocks.pop_back(); // 从空闲列表中移除
m_usedBlocks++; // 增加使用计数
return block; // 返回分配的块
}
// 释放内存块
void deallocate(void* block) {
assert(block != nullptr); // 确保要释放的块不是nullptr
m_freeBlocks.push_back(static_cast<char*>(block)); // 添加到空闲块列表
m_usedBlocks--; // 减少使用计数
}
// 获取当前使用的块数
size_t usedBlocks() const {
return m_usedBlocks;
}
// 获取空闲块的数量
size_t freeBlocks() const {
return m_freeBlocks.size();
}
private:
size_t m_blockSize; // 每个内存块的大小
size_t m_blockCount; // 内存池中的块数量
void* m_pool; // 内存池的起始地址
std::vector<void*> m_freeBlocks; // 存储空闲块的列表
size_t m_usedBlocks; // 当前使用的块数
};
class Bullet {
public:
Bullet(int x, int y) : m_x(x), m_y(y) {
std::cout << "Bullet created at (" << x << ", " << y << ")\n";
}
~Bullet() {
std::cout << "Bullet destroyed\n";
}
// 其他Bullet方法...
private:
int m_x, m_y; // 子弹位置
};
class BulletPool {
public:
BulletPool(size_t size) : m_pool(sizeof(Bullet), size) {}
Bullet* acquire(int x, int y) {
void* mem = m_pool.allocate();
if (!mem) return nullptr; // 如果没有可用的子弹,返回nullptr
return new (mem) Bullet(x, y); // 使用placement new创建Bullet
}
void release(Bullet* bullet) {
bullet->~Bullet(); // 显式调用析构函数
m_pool.deallocate(bullet); // 将内存块返回到池中
}
private:
MemoryPool m_pool; // 内存池实例
};
// 示例使用
int main() {
BulletPool bulletPool(5); // 创建一个可容纳5个子弹的池
Bullet* bullet1 = bulletPool.acquire(10, 20);
Bullet* bullet2 = bulletPool.acquire(15, 25);
bulletPool.release(bullet1); // 释放子弹
bulletPool.release(bullet2); // 释放子弹
return 0;
}
4.2 高性能数据处理
在需要处理大量小数据结构时,内存池可以显著提高性能:
#include <iostream>
#include <vector>
#include <stdexcept>
#include <cassert>
class MemoryPool {
public:
// 构造函数,初始化内存池
MemoryPool(size_t blockSize, size_t blockCount)
: m_blockSize(blockSize), m_blockCount(blockCount), m_usedBlocks(0) {
// 分配内存池
m_pool = malloc(blockSize * blockCount);
if (!m_pool) {
throw std::bad_alloc(); // 内存分配失败,抛出异常
}
// 初始化空闲块列表
for (size_t i = 0; i < blockCount; ++i) {
m_freeBlocks.push_back(static_cast<char*>(m_pool) + i * blockSize);
}
}
// 析构函数,释放内存池
~MemoryPool() {
free(m_pool);
}
// 分配内存块
void* allocate() {
// 如果没有可用的块,返回nullptr
if (m_freeBlocks.empty()) {
return nullptr; // 可以改进为扩展内存池
}
// 从空闲块列表中取出一个块
void* block = m_freeBlocks.back();
m_freeBlocks.pop_back(); // 从空闲列表中移除
m_usedBlocks++; // 增加使用计数
return block; // 返回分配的块
}
// 释放内存块
void deallocate(void* block) {
assert(block != nullptr); // 确保要释放的块不是nullptr
m_freeBlocks.push_back(static_cast<char*>(block)); // 添加到空闲块列表
m_usedBlocks--; // 减少使用计数
}
// 获取当前使用的块数
size_t usedBlocks() const {
return m_usedBlocks;
}
// 获取空闲块的数量
size_t freeBlocks() const {
return m_freeBlocks.size();
}
private:
size_t m_blockSize; // 每个内存块的大小
size_t m_blockCount; // 内存池中的块数量
void* m_pool; // 内存池的起始地址
std::vector<void*> m_freeBlocks; // 存储空闲块的列表
size_t m_usedBlocks; // 当前使用的块数
};
class Data {
public:
Data(int value) : m_value(value) {}
~Data() {}
// 数据处理方法...
private:
int m_value; // 数据值
};
class DataPool {
public:
DataPool(size_t size) : m_pool(sizeof(Data), size) {}
Data* create(int value) {
void* mem = m_pool.allocate();
return new (mem) Data(value); // 使用placement new创建数据对象
}
void destroy(Data* data) {
data->~Data(); // 显式调用析构函数
m_pool.deallocate(data); // 将内存块返回到池中
}
private:
MemoryPool m_pool; // 内存池实例
};
// 示例使用
int main() {
DataPool dataPool(100); // 创建一个可容纳100个Data对象的池
Data* data1 = dataPool.create(42);
Data* data2 = dataPool.create(99);
dataPool.destroy(data1); // 释放数据
dataPool.destroy(data2); // 释放数据
return 0;
}
7. 总结
内存池是一种有效的内存管理技术,通过预分配和集中管理内存块,提高了内存分配和释放的效率。尽管它增加了一定的复杂性,但在高性能和实时系统中,它的优势往往是不可忽视的。理解内存池的基本概念、设计思路和使用场景,有助于在适当的地方应用这一技术。
相关文章:
C++ 内存池(Memory Pool)详解
1. 基本概念 内存池是一种内存管理技术,旨在提高内存分配的效率。它通过预先分配一块大的内存区域(池),然后从中分配小块内存来满足应用程序的需求。这样可以减少频繁的内存分配和释放带来的性能开销。 2. 设计思路 内存池的设…...
css三角形:css画箭头向下的三角形
.arrow { position: absolute; bottom: 0; left: 50%; transform: translateX(-50%); width: 0; height: 0; border-style: solid; border-width: 8px 5px 0 5px; /* 上、左、下、右 */ bord…...
CSS属性 - animation
一、基本概念 animation是 CSS 中的一个属性,用于将通过keyframes规则定义的动画应用到元素上。它是一种简写属性,能够在一个声明中设置多个动画相关的子属性。 二、语法结构 基本语法为: animation: name duration timing - function de…...
昇思MindSpore进阶教程--在ResNet-50网络上应用二阶优化实践(下)
大家好,我是刘明,明志科技创始人,华为昇思MindSpore布道师。 技术上主攻前端开发、鸿蒙开发和AI算法研究。 努力为大家带来持续的技术分享,如果你也喜欢我的文章,就点个关注吧 文章上半部分请查看 在ResNet-50网络上应…...

基于大数据的Python+Django电影票房数据可视化分析系统设计与实现
目录 1 引言 2 系统需求分析 3 技术选型 4 系统架构设计 5 关键技术实现 6 系统实现 7 总结与展望 1 引言 随着数字媒体技术的发展,电影产业已经成为全球经济文化不可或缺的一部分。电影不仅是艺术表达的形式,更是大众娱乐的重要来源。在这个背景…...
实景三维技术对光伏产业的发展具有哪些优势?
实景三维技术对光伏产业的发展具有显著的优势,主要体现在提高选址准确性、优化用地规划、促进数据融合应用以及赋能文旅服务领域。 提高选址准确性:通过构建高精度的三维地形模型,结合卫星遥感、无人机测绘等技术手段,实景三维…...

四非人的保研之路,2024(2025届)四非计算机的保研经验分享(西南交通、苏大nlp、西电、北邮、山软、山计、电科、厦大等)
文章目录 一、个人背景二、夏令营北京邮电大学CS西南交通大学CS深圳大学CS苏州大学NLP南开大学CS 三、预推免北京邮电大学CS华东师范大学 CS和大数据电子科技大学 CS东北大学 CS厦门大学 信息学院山东大学 CS和SE西安电子科技大学 CS 四、个人经验五、上岸 一、个人背景 学校专…...

UE5.4.3 录屏回放系统ReplaySystem蓝图版
这是ReplaySystem的蓝图使用方法版,以第三人称模版为例,需要几个必须步骤 项目config内DefaultEngine.ini的最后添加: [/Script/Engine.GameEngine] NetDriverDefinitions(DefName"DemoNetDriver",DriverClassName"/Script/…...

ECCV 2024 | 融合跨模态先验与扩散模型,快手处理大模型让视频画面更清晰!
计算机视觉领域顶级会议 European Conference on Computer Vision(ECCV 2024)将于9月29日至10月4日在意大利米兰召开,快手音视频技术部联合清华大学所发表的题为《XPSR: Cross-modal Priors for Diffusion-based Image Super-Resolution》——…...

9--苍穹外卖-SpringBoot项目中Redis的介绍及其使用实例 详解
目录 Redis入门 Redis简介 Redis服务启动与停止 服务启动命令 Redis数据类型 5种常用数据类型介绍 各种数据类型的特点 Redis常用命令 字符串操作命令 哈希操作命令 列表操作命令 集合操作命令 有序集合操作命令 通用命令 在java中操作Redis Redis的Java客户端 …...

【EXCEL数据处理】000014 案例 EXCEL分类汇总、定位和创建组。附多个操作案例。
前言:哈喽,大家好,今天给大家分享一篇文章!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏关注哦 💕 目录 【EXCEL数据处理】000014 案例 EXCEL分类汇总、定位和创建组。附多个操…...
Windows环境Apache httpd 2.4 web服务器加载PHP8:Hello,world!
Windows环境Apache httpd 2.4 web服务器加载PHP8:Hello,world! (1)首先需要安装apache httpd 2.4 web服务器: Windows安装启动apache httpd 2.4 web服务器-CSDN博客文章浏览阅读222次,点赞5次&…...

Spring框架使用Api接口实现AOP的切面编程、两种方式的程序示例以及Java各数据类型及基本数据类型的默认值/最大值/最小值列表
一、Spring框架使用Api接口-继承类实现AOP的切面编程示例 要使用Spring框架AOP,除了要导入spring框架包外,还需要导入一个织入的包org.aspectj,具体maven依赖如下: <dependency><groupId>org.springframework</gr…...
【达梦数据库】尽可能 disql 的使用效果与异构数据库一致
文章目录 前言disql 效果优化参数设置参数说明 mysql参数设置参数说明 db2参数设置参数说明 待补充 前言 让达梦的disql 使用起来更跟手,与其他优质数据库的命令行工具通过配置参数的方式尽可能一致,提高使用体验,长期整理中~~~ 测试版本&…...
【研1深度学习】《神经网络和深度学习》阅读笔记(记录中......
9.27 语义鸿沟: 是指输入数据的底层特征和高层语义信息之间的不一致性和查一下。如果可以有一个好的表示在某种程度上能够反映出数据的高层语义特征,那么我们就能相对容易的构建后续的机器学习模型。嵌入(Embedding):…...

十一不停歇-学习ROS2第一天 (10.2 10:45)
话题通信 1.1 发布第一个节点: import rclpy #导入此类模块 rcl类型 from rclpy.node import Node #从这个子模块中导入这类函数 def main(): #定义这个函数 rclpy.init() #使用初始化函数 node Node(hello_python) 将类函数里面的内容调给…...
Java高效编程(14):考虑实现 `Comparable
解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 与其他方法不同,compareTo 并非 Object 类中声明的,而是 Comparable 接口的唯一方法。compareTo 方法与 equals 类似,但它不仅支持相等性比较,还允许顺序…...
华为昇腾CANN训练营2024第二季--Ascend C算子开发能力认证(中级)题目和经验分享
大家好,我是刘明,明志科技创始人,华为昇思MindSpore布道师。 技术上主攻前端开发、鸿蒙开发和AI算法研究。 努力为大家带来持续的技术分享,如果你也喜欢我的文章,就点个关注吧 正文开始 华为昇腾CANN训练营2024第二季…...
实战OpenCV之形态学操作
基础入门 形态学操作是一种基于图像形状的处理方法,主要用于结构分析,比如:边缘检测、轮廓提取、噪声去除等。这些操作通常使用一个称为“结构元素”(Structuring Element)的核来进行,结构元素可以是任何形状,但最常见的有矩形和圆形。形态学操作的核心在于通过结构元素…...
矩阵的特征值和特征向量
矩阵的特征值和特征向量是线性代数中非常重要的概念,用于描述矩阵对向量的作用,特别是在矩阵对向量的线性变换中的表现。它们帮助我们理解矩阵在某些方向上的缩放或旋转效果。 1. 特征值和特征向量的定义: 给定一个 n n n \times n nn 的…...

如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
NPOI Excel用OLE对象的形式插入文件附件以及插入图片
static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...

day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...
【Elasticsearch】Elasticsearch 在大数据生态圈的地位 实践经验
Elasticsearch 在大数据生态圈的地位 & 实践经验 1.Elasticsearch 的优势1.1 Elasticsearch 解决的核心问题1.1.1 传统方案的短板1.1.2 Elasticsearch 的解决方案 1.2 与大数据组件的对比优势1.3 关键优势技术支撑1.4 Elasticsearch 的竞品1.4.1 全文搜索领域1.4.2 日志分析…...
【FTP】ftp文件传输会丢包吗?批量几百个文件传输,有一些文件没有传输完整,如何解决?
FTP(File Transfer Protocol)本身是一个基于 TCP 的协议,理论上不会丢包。但 FTP 文件传输过程中仍可能出现文件不完整、丢失或损坏的情况,主要原因包括: ✅ 一、FTP传输可能“丢包”或文件不完整的原因 原因描述网络…...

渗透实战PortSwigger Labs指南:自定义标签XSS和SVG XSS利用
阻止除自定义标签之外的所有标签 先输入一些标签测试,说是全部标签都被禁了 除了自定义的 自定义<my-tag onmouseoveralert(xss)> <my-tag idx onfocusalert(document.cookie) tabindex1> onfocus 当元素获得焦点时(如通过点击或键盘导航&…...
深入理解 React 样式方案
React 的样式方案较多,在应用开发初期,开发者需要根据项目业务具体情况选择对应样式方案。React 样式方案主要有: 1. 内联样式 2. module css 3. css in js 4. tailwind css 这些方案中,均有各自的优势和缺点。 1. 方案优劣势 1. 内联样式: 简单直观,适合动态样式和…...

【Qt】控件 QWidget
控件 QWidget 一. 控件概述二. QWidget 的核心属性可用状态:enabled几何:geometrywindows frame 窗口框架的影响 窗口标题:windowTitle窗口图标:windowIconqrc 机制 窗口不透明度:windowOpacity光标:cursor…...

SOC-ESP32S3部分:30-I2S音频-麦克风扬声器驱动
飞书文档https://x509p6c8to.feishu.cn/wiki/SKZzwIRH3i7lsckUOlzcuJsdnVf I2S简介 I2S(Inter-Integrated Circuit Sound)是一种用于传输数字音频数据的通信协议,广泛应用于音频设备中。 ESP32-S3 包含 2 个 I2S 外设,通过配置…...