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

C++ 内存池(Memory Pool)详解

1. 基本概念

内存池是一种内存管理技术,旨在提高内存分配的效率。它通过预先分配一块大的内存区域(池),然后从中分配小块内存来满足应用程序的需求。这样可以减少频繁的内存分配和释放带来的性能开销。

2. 设计思路

内存池的设计通常遵循以下步骤:

  • 预分配内存:在程序开始时,预先分配一块较大的内存区域。
  • 管理空闲块:使用链表、栈或数组等数据结构管理可用内存块。
  • 分配和释放:提供分配和释放接口,让用户从内存池中获取和释放内存。
  • 回收机制:当内存块被释放时,将其返回到内存池中,便于后续使用。

3. 原理

内存池的核心原理是降低内存分配的时间复杂度。标准的 newdelete 操作在需要内存时会与操作系统频繁交互,可能会造成较大的开销。而内存池将这种频繁操作集中到池的初始化阶段,后续的分配和释放则在池内进行,速度更快。

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. 基本概念 内存池是一种内存管理技术&#xff0c;旨在提高内存分配的效率。它通过预先分配一块大的内存区域&#xff08;池&#xff09;&#xff0c;然后从中分配小块内存来满足应用程序的需求。这样可以减少频繁的内存分配和释放带来的性能开销。 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 中的一个属性&#xff0c;用于将通过keyframes规则定义的动画应用到元素上。它是一种简写属性&#xff0c;能够在一个声明中设置多个动画相关的子属性。 二、语法结构 基本语法为&#xff1a; animation: name duration timing - function de…...

昇思MindSpore进阶教程--在ResNet-50网络上应用二阶优化实践(下)

大家好&#xff0c;我是刘明&#xff0c;明志科技创始人&#xff0c;华为昇思MindSpore布道师。 技术上主攻前端开发、鸿蒙开发和AI算法研究。 努力为大家带来持续的技术分享&#xff0c;如果你也喜欢我的文章&#xff0c;就点个关注吧 文章上半部分请查看 在ResNet-50网络上应…...

基于大数据的Python+Django电影票房数据可视化分析系统设计与实现

目录 1 引言 2 系统需求分析 3 技术选型 4 系统架构设计 5 关键技术实现 6 系统实现 7 总结与展望 1 引言 随着数字媒体技术的发展&#xff0c;电影产业已经成为全球经济文化不可或缺的一部分。电影不仅是艺术表达的形式&#xff0c;更是大众娱乐的重要来源。在这个背景…...

实景三维技术对光伏产业的发展具有哪些优势?

实景三维技术对光伏产业的发展具有显著的优势&#xff0c;主要体现在提高选址准确性、优化用地规划、促进数据融合应用以及赋能文旅服务领域。‌ 提高选址准确性‌&#xff1a;通过构建高精度的三维地形模型&#xff0c;结合卫星遥感、无人机测绘等技术手段&#xff0c;实景三维…...

四非人的保研之路,2024(2025届)四非计算机的保研经验分享(西南交通、苏大nlp、西电、北邮、山软、山计、电科、厦大等)

文章目录 一、个人背景二、夏令营北京邮电大学CS西南交通大学CS深圳大学CS苏州大学NLP南开大学CS 三、预推免北京邮电大学CS华东师范大学 CS和大数据电子科技大学 CS东北大学 CS厦门大学 信息学院山东大学 CS和SE西安电子科技大学 CS 四、个人经验五、上岸 一、个人背景 学校专…...

UE5.4.3 录屏回放系统ReplaySystem蓝图版

这是ReplaySystem的蓝图使用方法版&#xff0c;以第三人称模版为例&#xff0c;需要几个必须步骤 项目config内DefaultEngine.ini的最后添加&#xff1a; [/Script/Engine.GameEngine] NetDriverDefinitions(DefName"DemoNetDriver",DriverClassName"/Script/…...

ECCV 2024 | 融合跨模态先验与扩散模型,快手处理大模型让视频画面更清晰!

计算机视觉领域顶级会议 European Conference on Computer Vision&#xff08;ECCV 2024&#xff09;将于9月29日至10月4日在意大利米兰召开&#xff0c;快手音视频技术部联合清华大学所发表的题为《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分类汇总、定位和创建组。附多个操作案例。

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 【EXCEL数据处理】000014 案例 EXCEL分类汇总、定位和创建组。附多个操…...

Windows环境Apache httpd 2.4 web服务器加载PHP8:Hello,world!

Windows环境Apache httpd 2.4 web服务器加载PHP8&#xff1a;Hello&#xff0c;world&#xff01; &#xff08;1&#xff09;首先需要安装apache httpd 2.4 web服务器&#xff1a; Windows安装启动apache httpd 2.4 web服务器-CSDN博客文章浏览阅读222次&#xff0c;点赞5次&…...

Spring框架使用Api接口实现AOP的切面编程、两种方式的程序示例以及Java各数据类型及基本数据类型的默认值/最大值/最小值列表

一、Spring框架使用Api接口-继承类实现AOP的切面编程示例 要使用Spring框架AOP&#xff0c;除了要导入spring框架包外&#xff0c;还需要导入一个织入的包org.aspectj&#xff0c;具体maven依赖如下&#xff1a; <dependency><groupId>org.springframework</gr…...

【达梦数据库】尽可能 disql 的使用效果与异构数据库一致

文章目录 前言disql 效果优化参数设置参数说明 mysql参数设置参数说明 db2参数设置参数说明 待补充 前言 让达梦的disql 使用起来更跟手&#xff0c;与其他优质数据库的命令行工具通过配置参数的方式尽可能一致&#xff0c;提高使用体验&#xff0c;长期整理中~~~ 测试版本&…...

【研1深度学习】《神经网络和深度学习》阅读笔记(记录中......

9.27 语义鸿沟&#xff1a; 是指输入数据的底层特征和高层语义信息之间的不一致性和查一下。如果可以有一个好的表示在某种程度上能够反映出数据的高层语义特征&#xff0c;那么我们就能相对容易的构建后续的机器学习模型。嵌入&#xff08;Embedding&#xff09;&#xff1a;…...

十一不停歇-学习ROS2第一天 (10.2 10:45)

话题通信 1.1 发布第一个节点&#xff1a; import rclpy #导入此类模块 rcl类型 from rclpy.node import Node #从这个子模块中导入这类函数 def main(): #定义这个函数 rclpy.init() #使用初始化函数 node Node(hello_python) 将类函数里面的内容调给…...

Java高效编程(14):考虑实现 `Comparable

解锁Python编程的无限可能&#xff1a;《奇妙的Python》带你漫游代码世界 与其他方法不同&#xff0c;compareTo 并非 Object 类中声明的&#xff0c;而是 Comparable 接口的唯一方法。compareTo 方法与 equals 类似&#xff0c;但它不仅支持相等性比较&#xff0c;还允许顺序…...

华为昇腾CANN训练营2024第二季--Ascend C算子开发能力认证(中级)题目和经验分享

大家好&#xff0c;我是刘明&#xff0c;明志科技创始人&#xff0c;华为昇思MindSpore布道师。 技术上主攻前端开发、鸿蒙开发和AI算法研究。 努力为大家带来持续的技术分享&#xff0c;如果你也喜欢我的文章&#xff0c;就点个关注吧 正文开始 华为昇腾CANN训练营2024第二季…...

实战OpenCV之形态学操作

基础入门 形态学操作是一种基于图像形状的处理方法,主要用于结构分析,比如:边缘检测、轮廓提取、噪声去除等。这些操作通常使用一个称为“结构元素”(Structuring Element)的核来进行,结构元素可以是任何形状,但最常见的有矩形和圆形。形态学操作的核心在于通过结构元素…...

矩阵的特征值和特征向量

矩阵的特征值和特征向量是线性代数中非常重要的概念&#xff0c;用于描述矩阵对向量的作用&#xff0c;特别是在矩阵对向量的线性变换中的表现。它们帮助我们理解矩阵在某些方向上的缩放或旋转效果。 1. 特征值和特征向量的定义&#xff1a; 给定一个 n n n \times n nn 的…...

大型活动交通拥堵治理的视觉算法应用

大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动&#xff08;如演唱会、马拉松赛事、高考中考等&#xff09;期间&#xff0c;城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例&#xff0c;暖城商圈曾因观众集中离场导致周边…...

1688商品列表API与其他数据源的对接思路

将1688商品列表API与其他数据源对接时&#xff0c;需结合业务场景设计数据流转链路&#xff0c;重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点&#xff1a; 一、核心对接场景与目标 商品数据同步 场景&#xff1a;将1688商品信息…...

跨链模式:多链互操作架构与性能扩展方案

跨链模式&#xff1a;多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈&#xff1a;模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展&#xff08;H2Cross架构&#xff09;&#xff1a; 适配层&#xf…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...

css的定位(position)详解:相对定位 绝对定位 固定定位

在 CSS 中&#xff0c;元素的定位通过 position 属性控制&#xff0c;共有 5 种定位模式&#xff1a;static&#xff08;静态定位&#xff09;、relative&#xff08;相对定位&#xff09;、absolute&#xff08;绝对定位&#xff09;、fixed&#xff08;固定定位&#xff09;和…...

select、poll、epoll 与 Reactor 模式

在高并发网络编程领域&#xff0c;高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表&#xff0c;以及基于它们实现的 Reactor 模式&#xff0c;为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。​ 一、I…...

聊一聊接口测试的意义有哪些?

目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开&#xff0c;首…...

PAN/FPN

import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...

Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)

引言 在人工智能飞速发展的今天&#xff0c;大语言模型&#xff08;Large Language Models, LLMs&#xff09;已成为技术领域的焦点。从智能写作到代码生成&#xff0c;LLM 的应用场景不断扩展&#xff0c;深刻改变了我们的工作和生活方式。然而&#xff0c;理解这些模型的内部…...

【免费数据】2005-2019年我国272个地级市的旅游竞争力多指标数据(33个指标)

旅游业是一个城市的重要产业构成。旅游竞争力是一个城市竞争力的重要构成部分。一个城市的旅游竞争力反映了其在旅游市场竞争中的比较优势。 今日我们分享的是2005-2019年我国272个地级市的旅游竞争力多指标数据&#xff01;该数据集源自2025年4月发表于《地理学报》的论文成果…...