千家信息网

C++内存池如何实现

发表于:2026-11-02 作者:千家信息网编辑
千家信息网最后更新 2026年11月02日,这篇文章主要介绍了C++内存池如何实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇C++内存池如何实现文章都会有所收获,下面我们一起来看看吧。一、内存池基础知识1、什么
千家信息网最后更新 2026年11月02日C++内存池如何实现

这篇文章主要介绍了C++内存池如何实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇C++内存池如何实现文章都会有所收获,下面我们一起来看看吧。

一、内存池基础知识

1、什么是内存池

1.1 池化技术

池化技术是计算机中的一种设计模式,主要是指:将程序中经常要使用的计算机资源预先申请出来,由程序自己管理,程序在使用时直接从"池"中获取,不仅保证了程序占有的资源数量同时减少资源的申请和释放时间。常见的池化技术有内存池、线程池、连接池等。

1.2 内存池

内存池是一种动态内存分配与管理技术。它的核心思想是:预先申请一段内存空间,使用一种高效的数据结构(哈希、链表)进行管理,当程序需要内存时直接从内存池中分配一块内存给程序,同样当使用完时在归还给内存池。这样做的好处是,减少直接使用new/delete、malloc/free等API申请和释放内存的时间,提高程序运行效率;同时,程序每次直接使用new/delete、malloc/free从内存中申请空间,会导致内存碎片问题,内存池直接申请大块内存就减少了内存碎片。

2、内存池的作用

2.1 效率问题

通常申请内存都是通过new/delete、malloc/free接口直接从内存的堆区申请一块内存,释放也是直接释放到堆中。频繁的申请和释放必然消耗大量时间,降低程序的运行效率。

例如:假设每个链表的节点大小为16字节,当链表需要经常插入节点时,必然就需要频繁的内存申请操纵,每次从堆中申请16个字节都要一定的时间开销,释放内存也需要时间开销。使用内存池,我们可以直接从内存中申请"一批节点",当程序需要内存时不用直接去堆中申请,直接将预先申请好的内存分配给程序。

2.2 内存碎片

频繁的从内存中申请小块内存会导致内存碎片问题。内存碎片分为内碎片和外碎片两种。

1)外碎片

外碎片也就是我们常说的内存碎片。例如:我们每次从内存中申请一块16字节大小的内存,内存中就会存在很多16个字节大小的块,当该内存释放时就可能造成内存碎片,如下图:

内存中空闲内存大小为88字节,但是我们能申请的最大内存块为21字节。

2)内碎片

内碎片是指已经分配出去的内存中存在的未使用的小块内存。内存池技术虽然解决了内存随便但是又造成了内碎片问题,内碎片不可避免但是可以通过程序的优化减少内存内碎片。

例如:实际需要是申请10byte的内存,定长内存池可能会进行内存对齐,一次性分配了16个字节的内存,多余的6字节实际并未使用,这6字节就是内存内碎片。

3、内存池技术的演进

1)最最最最"简单"的内存池

做一个链表,指向空闲的内存。分配就是从链表中取出来一块返回pop,释放就是将内存在push到链表中。需要做好归并,标记和保护,防止内存二次释放问题。

2)定长内存池

实现一个FreeList类,它的本质是一个链表,节点是一块固定大小的内存,采用头插和头删的方式申请释放内存。每个固定内存分配器里面有两个链表:OpenList用于存储未分配的空闲内存对象(FreeList对象),CloseList用于存储已经分配的内存对象。

分配内存就是从IOpenLsit中取出一个对象给程序,释放内存就是将对象push到CloseList里。当内存不够时,OpenList申请一个大块内存在切割成固定的长度大小的小块内存。

3)C++STL库中的内存池

定长内存池存在的问题就是只能申请固定长度的内存,而实际中我们需要申请的内存大小可能是不管固定,在C++STL库中,采用哈希表和定长内存池结合的方式实现了一个内存池。具体如下

构造多个定长内存池,以一个固定的对齐数进行对齐(例如以8字节进行对齐),第一个定长内存池的内存对象大小为8(至少得能保证无论在64位还是32位系统下都可以保存下一个指针类型),第二个内存池对象大小为16...最后一个内存池对象大小为128byte,当申请的内存大小超过128字节时,通过二级空间配置器申请(直接从内存中申请)。

构造一个哈希表,将不同大小的内存对象挂在哈希表中。如下图:

申请内存:加入要申请的内存大小为8字节直接在index = 0处分配一块内存,当然申请的内存小于8字节时也会直接分配8字节的内存。当Free_list[index]为nullptr时从内存中申请一块内存,切割成固定大小'挂在"Free_list[index]位置。

释放内存:根据内存对象大小,计算index在插入到哈希表中的index位置。

二、简易内存池原理

1、整体设计

1.1 内存池结构

两个链表,RequestMemory和ReleaseMemory。

RequestMemory链表存储的是使用new或者malloc从物理内存申请的还没有被使用的内存块,是一个个的memNode节点。

ReleaseMemory链表存储的是使用完释放回来的固定大小的内存块。

1.2 申请内存

  • 先在ReleaseMemory找,如果有内存则直接pop使用

  • ReleaseMemory为nullptr时,在RequestMemory中找。

  • RequestMemory的头节点表示的是新申请的,申请内存时只需要在头结点中找,判断头结点的useCount和sumCount是否相等。当useCount等于sumCount时表示已经用完了,就需要去物理内存中申请,否则直接从表头push一块。

  • 去物理内存申请内存时,申请的大小是上一次申请内存块大小的二倍,并将申请的内存块push到RequestMemory头部。

1.3 释放内存

释放内存时,直接将要释放的内存push到ReleaseMemory的头部即可。

2、详细剖析

2.1 blockNode结构

blockNode表示一个个新申请的内存块,用一个结构体进行管理。blockNode成员如下:

  • void* _memory:表示新申请的内存块的首地址

  • BlockNode * _next:存储next节点

  • _objNum:内存块对象的个数

注意:blockNode的大小每次都是上一次的二倍,是一个质数增长,因此应该设置一个上限,当到达一定大小后进行线性增长。这里规定,最大内存块的大小为100000*sizeof(T),T表示的是申请的节点类型。

2.2 单个对象的大小

这里的单个对象指的ReleaseMemory的节点大小,当用户申请的内存大小sizeof(T)小于sizeof(T*)时,为了能够将该对象链接到ReleaseMemory中,应该按照T*进行分配。

3、性能比较

分别使用malloc/free、new/delete、memPool申请和释放110000个内存,时间如下:

三、简易内存池完整源码

#include#include#includeusing namespace std; templateclass MemPool{private:        //内存块结构        typedef struct BlockNode        {                void* _memory;//内存块地址                BlockNode* _next;//下一个blockNode                size_t _objNum;//内存块对象的个数                //构造函数---num表示申请对象的个数                BlockNode(size_t num)                        :_objNum(num),                        _next(nullptr)                {                        _memory = malloc(_objNum*_size);                }                 ~BlockNode()                {                        free(_memory);                        _memory = nullptr;                        _next = nullptr;                        _objNum = 0;                }        }BlockNode;protected:        static size_t _size;//单个对象的大小        T* _releaseMemory = nullptr;//释放的内存        BlockNode* _requestMemory;//申请的内存块        size_t _maxNum;//内存块最大的大小        size_t _useCount;//当前内存块已经使用的对象个数protected:        //设置单个对象的大小        static size_t setSize()        {                return (sizeof(T) >= sizeof(T*) ? sizeof(T):sizeof(T*));        }public:        MemPool()                :_useCount(0),                _releaseMemory(nullptr),                _maxNum(100000*_size)        {                //开始先申请32个_size大小的空间                _requestMemory = new BlockNode(32);        }         ~MemPool()        {                BlockNode *cur = _requestMemory;                while (cur)                {                        BlockNode* del = cur;                        cur = cur->_next;                        delete del;            //会自动调用~BlockNode()                }        }         T* New()        {                //先在releaseMemory中找                if (_releaseMemory)                {                        T* obj = _releaseMemory;                        _releaseMemory = *((T**)_releaseMemory);//releaseMemory的前几个字节存储的是下一个节点的地址                        return obj;                }                else                {                        //判断requesetMemory中是否还有空闲内存                        if (_requestMemory->_objNum == _useCount)                        {                                //取物理内存中申请一块内存                                size_t size = 2 * _useCount >= _maxNum ? _maxNum : 2 * _useCount;                                BlockNode* newBlock = new BlockNode(size);                                 newBlock->_next = _requestMemory;                                _requestMemory = newBlock;                                _useCount = 0;                        }                        //走到这里,一定有内存                        T* obj = (T*)((char*)_requestMemory->_memory+_useCount*_size);                         _useCount++;                        return new(obj)T();//用定位new对这块空间初始化                }        }         void Delete(T* obj)        {                if (obj)                {                        obj->~T();                         *((T**)obj) = _releaseMemory;                        _releaseMemory = obj;                }        }}; //静态成员变量,类外初始化templatesize_t MemPool::_size = MemPool::setSize(); struct TreeNode{        int _val;        TreeNode* _left;        TreeNode* _right;};void test1(){        MemPool mp;         vector v;        for (int i = 0; i < 10; i++)        {                TreeNode* mem = mp.New();                v.push_back(mem);        }         for (int i = 0; i < 10; i++)        {                mp.Delete(v[i]);        }}

关于"C++内存池如何实现"这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对"C++内存池如何实现"知识都有一定的了解,大家如果还想学习更多知识,欢迎关注行业资讯频道。

内存 大小 对象 碎片 字节 程序 分配 节点 技术 时间 问题 定长 存储 空间 结构 哈希 C++ 个数 单个 就是 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 哈尔滨大商圈互联网科技有限公司 中关村互联网科技集团 科技与互联网 互联网科技卖菜 腾讯阿里互联网科技 北京互联网科技有限公司 北京互联网络科技有限公司 壹账通互联网科技 上海致骋互联网科技 天扬科技互联网广告 山东吉时兔互联网科技有限公司 华中科技有哪些互联网大佬 温州喔噻互联网科技有限公司 来此购互联网科技有限公司 产业互联网科技服务公司 互联网科技包装股份有限公司上市 九思连强互联网电子科技有限公司 互联网科技手抄报图片 北京天晋互联网科技有限公司 保山浩联互联网科技有限公司 互联网新科技 高科技互联网 顺玮阁互联网科技 联恩商钥互联网科技有限公司 科技赋能互联网健康 岳阳华臻互联网科技有限公司 崇明区互联网教育科技诚信经营 互联网时代的科技公司 2021世界互联网领先科技成果发布 广州全民伙伴互联网科技有限公司
0