内存池详解

本文深入解析内存池的概念、分配及释放内存的过程,并讨论其在STL中的实现方式。内存池通过减少空间碎片来提高内存利用率,但同时也存在一定的局限性。

内存池详解

概念

如图所示,内存池可以减少大量的调用new malloc产生的空间碎片,提高内存的利用率

STL中一共有两级空间分配器,内存池就是实现在二级空间分配器当中的

内存池里面一共维持中16根链表,每一个链表负责特定大小的空间区块,从8byte128byte

分配内存的过程

当我们传入一个字节的参数表示我们所需要的内存的大小的时候,自动校对到第几号链表,比如13byte会分配16byte,找到对应的几号链表以后

  1. 判断链表是否为空,如果不为空,那么直接从free_list中拔出来分配,同时指针后移,如果 为空进行2
  2. 2.1 如果链表为空内存池不为空时先判断内存池里面的剩余空间是否满足,20×节点所需大小的空间,如果足够就直接从内存池拿出20个节点大小的空间,其中一个分配给用户剩下的19个挂在对应的链表上
    2.2 如果不够20个节点的大小就会看看是否满足1个节点的大小,如果够的话,分配给用户,然后剩余空间尽量的挂在free_list下面
  3. 如果剩余空间连一个node都无法满足此时二级空间适配器会使用mallocheap中申请内存,申请的大小就是2*20*node + 一段额外空间其中一半留给内存池,一半留在内存池

内存的释放

如果大于128byte直接调用一级空间分配器,否则直接挂在相应的合适的链表下面

内存池的优缺点

优点

  • 解决了外部碎片的问题,提高了空间利用率

缺点

  • 因为是以8的倍数进行内存的管理,所以如果只需要1个byte那么就会造成浪费
  • 二级空间分配器是在堆中申请大块的狭义内存池,在程序的执行过程中,申请的内存一块一块的挂在自由链表上,也就是不会还给操作系统,并且内存池的实现是全员静态的,也就是只有当程序结束了,内存才会真正的释放还给操作系统。这就会造成以下问题
  1. 当我们不断的开辟小的内存,那么在不断的开辟下,最后整个堆的空间都挂在自由链表上,这样之后想申请大的空间就不行了
  2. 如果自由链表上很多内存块都没有使用,当前的进程又占着内存不放,所以如果有大量的内存池在使用,就会造成极大的资源浪费
### Linux C++ 内存池实现详解 #### 什么是内存池内存池是一种预先分配固定大小的连续内存区域的技术,旨在减少频繁调用 `malloc` 或 `new` 的开销。它通过一次性分配一大块内存,并将其划分为多个较小的单元来供后续使用,从而显著提高性能[^2]。 --- #### 内存池的设计目标 1. **降低碎片化**:通过预分配大块内存,避免因多次动态分配而导致的内存碎片。 2. **加速分配与释放**:相比传统的 `malloc/free` 或 `new/delete`,内存池能够提供更快的内存分配和回收速度。 3. **简化资源管理**:统一管理和销毁整个内存池中的对象,减少了手动干预的可能性。 --- #### 内存池的基本组成 一个典型的内存池通常由以下几个部分构成: 1. **内存块 (Memory Block)**:存储实际的数据。 2. **自由链表 (Free List)**:记录可用内存块的位置。 3. **元数据 (Metadata)**:描述每个内存块的状态(已分配/未分配)以及其大小等信息。 --- #### 实现步骤 下面是一个简单的内存池实现方案: ##### 1. 头文件定义 (`memory_pool.hpp`) ```cpp #ifndef MEMORY_POOL_HPP #define MEMORY_POOL_HPP #include <cstdlib> #include <iostream> class MemoryPool { public: struct Node { Node* next; }; explicit MemoryPool(size_t blockSize, size_t poolSize); ~MemoryPool(); void* allocate(); void deallocate(void* ptr); private: size_t _blockSize; size_t _poolSize; char* _buffer; Node* _freeList; void initializePool(); }; #endif // MEMORY_POOL_HPP ``` ##### 2. 源文件实现 (`memory_pool.cpp`) ```cpp #include "memory_pool.hpp" // 构造函数:初始化内存池 MemoryPool::MemoryPool(size_t blockSize, size_t poolSize) : _blockSize(blockSize), _poolSize(poolSize) { _buffer = static_cast<char*>(std::malloc(_blockSize * _poolSize)); if (!_buffer) { throw std::bad_alloc(); } initializePool(); } // 析构函数:释放内存池 MemoryPool::~MemoryPool() { std::free(_buffer); } // 初始化自由链表 void MemoryPool::initializePool() { _freeList = reinterpret_cast<Node*>(_buffer); for (size_t i = 0; i < _poolSize - 1; ++i) { _freeList[i].next = &_freeList[i + 1]; } _freeList[_poolSize - 1].next = nullptr; } // 分配内存 void* MemoryPool::allocate() { if (_freeList == nullptr) { return nullptr; // 如果没有剩余空间,则返回空指针 } Node* node = _freeList; _freeList = _freeList->next; return node; } // 释放内存 void MemoryPool::deallocate(void* ptr) { Node* node = reinterpret_cast<Node*>(ptr); node->next = _freeList; _freeList = node; } ``` --- #### 使用示例 ```cpp #include "memory_pool.hpp" #include <iostream> struct Data { int value; }; int main() { try { MemoryPool pool(sizeof(Data), 10); // 创建一个可以容纳10个Data结构体的内存池 Data* data1 = static_cast<Data*>(pool.allocate()); if (data1 != nullptr) { data1->value = 42; std::cout << "Allocated data with value: " << data1->value << std::endl; } Data* data2 = static_cast<Data*>(pool.allocate()); if (data2 != nullptr) { data2->value = 84; std::cout << "Allocated data with value: " << data2->value << std::endl; } pool.deallocate(data1); // 手动释放第一个对象 std::cout << "Deallocated first object." << std::endl; Data* reusedData = static_cast<Data*>(pool.allocate()); // 重新利用被释放的对象 if (reusedData != nullptr) { reusedData->value = 168; std::cout << "Reused memory with new value: " << reusedData->value << std::endl; } } catch (const std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; } return 0; } ``` --- #### 关键点解析 1. **自由链表的作用** 自由链表用于跟踪哪些内存块是可用的。每次分配时从链表头部取出节点;释放时将节点插回链表头部[^2]。 2. **线程安全性** 上述实现并未考虑多线程环境下的同步问题。如果需要支持多线程访问,可以通过加锁机制(如互斥量 `std::mutex`)保护 `_freeList` 的操作[^4]。 3. **扩展性** 此简单版本仅适用于同尺寸的小型对象分配。对于不同尺寸的对象需求,可以设计分层或多级内存池体系。 --- #### 性能优化建议 - **批量分配**:当请求大量相同类型的对象时,可以从内存池中一次性获取一批内存块,而不是逐个分配。 - **缓存行对齐**:为了减少伪共享现象,可以在每块内存之间增加填充字节以确保它们位于不同的缓存行上[^4]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值