在使用内存池进行内存管理之前,通常使用new/malloc或者delete/free来申请或者释放内存。在这个过程中,系统要首先查找内部维护的内存空闲块表,并且需要根据比如LRU等内存分配算法找到合适大小的空闲内存块。如果该空闲内存块过大,还需要切割成已分配的部分和较小的空闲块。然后系统更新内存空闲块表,完成一次内存分配。类似地,在释放内存时,系统把释放的内存块重新加入到空闲内存块表中。如果有可能的话,可以把相邻的空闲块合并成较大的空闲块。默认的内存管理函数还考虑到多线程的应用,需要在每次分配和释放内存时加锁,同样增加了开销。

由此可见,当申请的内存块大小不定,申请和释放时间也不相同。 所以这个堆(连续的整个内存块) 会形成零零散散的情况(称为内存碎片)。当频繁使用时会造成大量的内存碎片并进而降低性能;并且在堆中分配内存时,需要查询查询已分配链表和空闲块链表,查询这两张表需要时间,所以如果申请和释放堆内存比较频繁的话,会比较浪费时间。基于上面两点,考虑使用内存池的方式,进行内存块的管理,减小分配和回收的开销。

内存池实现的步骤:

(1)首先申请一块空间当做初始的内存池,这块空间容纳一部分对象。
(2)定义一个内存节点为一块链表空间加上指向下一块链表节点的指针,即Memory Node。各个空闲的内存节点通过指针形成一个链表,链表的每一个内存节点都是一块可供分配的内存空间;
(3)定义一个指针freeNodeHeader为可用的空闲结点的头指针。每当一块MemoryNode被分配出去,FreeNodeHeader下移,那么这块空间就认为被占用了。
(4)一个MemoryNode被回收,那么就重新放入FreeNodeHeader指向的链表首位,也就是重新加入空闲链表。

(5)如果空闲区不够用了,那么新建一个内存块,内存块上有新的内存节点,接着进行分配就行。这也就体现出了“内存池”的作用。

如上图所示,内存块有指针指向这个内存块的真正可以分配的内存空间,也就是MemoryNode。BlockHeader用于指向第一个内存块,方便对内存块进行管理。FreeNodeHeader用于真正管理内存节点。每次分配的时候,FreeNodeHeader下移一位,表示这个节点被分配出去了。当归还一个节点,则上移一位,把这个节点插入链表中就可以了。

实现的源代码:

#include<iostream>
#include<vector>
#include<string>
#include<pthread.h>
using namespace std;template<int ObjectSize, int NumofObjects = 20>
class MemPool
{
private:pthread_mutex_t mutex;//注意如果是多线程需要加锁//memory block内存块结构体struct MemBlock{MemBlock* pNext;FreeNode data[NumofObjects];};//freeNode空闲节点结struct FreeNode{FreeNode* pNext;char data[ObjectSize];};FreeNode* freeNodeHeader;MemBlock* memBlockHeader;public:MemPool(){freeNodeHeader = NULL;memBlockHeader = NULL;}// 内存池析构函数,删除每一个内存块节点~MemPool(){MemBlock* ptr;while (memBlockHeader){ptr = memBlockHeader->pNext;delete memBlockHeader;memBlockHeader = ptr;}}//用于分配一个可用的空闲结点void* malloc();//回收一个空闲结点void free(void*);
};//分配空闲的节点
template<int ObjectSize, int NumofObjects>
void* MemPool<ObjectSize, NumofObjects>::malloc()
{//加锁pthread_mutex_lock(&mutex);//无空闲节点,可能是第一次申请,也可能是之前申请的都用完了if (freeNodeHeader == NULL){MemBlock* newBlock = new MemBlock;newBlock->pNext = NULL;//freeNodeHeader指向内存块的第一个空闲区freeNodeHeader = &newBlock->data[0];     //将这个内存块的所有空闲结点串起来for (int i = 1; i < NumofObjects; ++i){newBlock->data[i - 1].pNext = &newBlock->data[i];}newBlock->data[NumofObjects - 1].pNext = NULL;//首次申请内存块,那么他就是第一块if (memBlockHeader == NULL){memBlockHeader = newBlock;}else{//将新内存块加入到内存块链表头部//其实插入尾部也可以,本身看有没有空闲内存块只要看FreeNodeHeader是不是空就行,所以对于menBlockHeader并没有前后之分newBlock->pNext = memBlockHeader;memBlockHeader = newBlock;}}//返回空节点闲链表的第一个节点void* freeNode = freeNodeHeader;freeNodeHeader = freeNodeHeader->pNext;//去锁pthread_mutex_unlock(&mutex);return freeNode;
}//释放已经分配的节点,直接插入freeNode队首即可
template<int ObjectSize, int NumofObjects>
void MemPool<ObjectSize, NumofObjects>::free(void* p)
{//先加锁pthread_mutex_lock(&mutex);FreeNode* pNode = (FreeNode*)p;pNode->pNext = freeNodeHeader; //将释放的节点插入空闲节点头部freeNodeHeader = pNode;pthread_mutex_unlock(&mutex);
}//我们用到的用于向内存池申请空间的类
class ActualClass
{static int count;int No;public:ActualClass(){No = count;count++;}void print(){cout << this << ": ";cout << "the " << No << "th object" << endl;}//向内存池申请和删除空间的方法void* operator new(size_t size);void operator delete(void* p);
};//定义内存池对象
MemPool<sizeof(ActualClass), 2> mp;//申请一块大小为sizeof(ActualClass)大小的空间,返回指针指向这块空间
void* ActualClass::operator new(size_t size)
{return mp.malloc();
}//删除这块申请的空间
void ActualClass::operator delete(void* p)
{mp.free(p);
}int ActualClass::count = 0;//测试内存池的函数
void AchieveMemPool(){//申请一块sizeof(ActualClass)大小的内存空间,指针p1指向这块空间ActualClass* p1 = new ActualClass;p1->print();//同样申请一块sizeof(ActualClass)大小的内存空间ActualClass* p2 = new ActualClass;p2->print();delete p1;p1 = new ActualClass;p1->print();delete p1;delete p2;
}

C++ 实现一个简单内存池相关推荐

  1. 一个轻量级内存池的实现与细节

    引言 内存池作为一种的内存管理机制被广泛地运用于各种领域当中,内存池拥有快速的内存分配与更加健壮的管理机制,同时在不同的平台与环境当中也拥有不同的实现方式,本文提出一种轻量级的内存池实现,可以非常方便 ...

  2. nginx源码分析—内存池结构ngx_pool_t及内存管理

    本博客( http://blog.csdn.net/livelylittlefish)贴出作者(阿波)相关研究.学习内容所做的笔记,欢迎广大朋友指正! Content 0.序 1.内存池结构 1.1 ...

  3. C++ 应用程序性能优化,第 6 章:内存池

    引言 本书主要针对的是 C++ 程序的性能优化,深入介绍 C++ 程序性能优化的方法和实例.全书由 4 个篇组成,第 1 篇介绍 C++ 语言的对象模型,该篇是优化 C++ 程序的基础:第 2 篇主要 ...

  4. nginx源码分析—内存池结构ngx_pool_t及内存管理(精辟)

    Content 0.序 1.内存池结构 1.1 ngx_pool_t结构 1.2其他相关结构 1.3 ngx_pool_t的逻辑结构 2.内存池操作 2.1创建内存池 2.2销毁内存池 2.3重置内存 ...

  5. apr_pool -- 内存池

    这个指南主要介绍如何使用 libapr ( apache portable runtime ). 版权所有, Copyright (C) 2005 INOUE Seiichiro <inoue& ...

  6. Linux多线程实践(9) --简单线程池的设计与实现

    线程池的技术背景 在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源.在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收.所以 ...

  7. 从结构体、内存池初始化到申请释放,详细解读鸿蒙轻内核的动态内存管理

    摘要:本文带领大家一起剖析了鸿蒙轻内核的动态内存模块的源代码,包含动态内存的结构体.动态内存池初始化.动态内存申请.释放等. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列九 动态内存Dyna ...

  8. Linux内存池技术

    看到一篇关于内存池技术的介绍文章,受益匪浅,转贴至此. 原贴地址:http://www.ibm.com/developerworks/cn/linux/l-cn-ppp/index6.html 6.1 ...

  9. c++内存池工作原理

    6.1 自定义内存池性能优化的原理 如前所述,读者已经了解到"堆"和"栈"的区别.而在编程实践中,不可避免地要大量用到堆上的内存.例如在程序中维护一个链表的数据 ...

最新文章

  1. 2018-2019-1 20189221 《深入理解计算机系统》第 1 周学习总结
  2. java 之持久化和序列化(反序列化)
  3. CentOS 7 系列(四)系统服务配置 服务(Service)
  4. SpringSecurity认证流程回顾
  5. OC类导入Swift工程演示
  6. c语言string最大长度,求3个字符串中最长单词的长度 求救 会一个的
  7. 贪小便宜的人交不得,否则,可能会因小失大
  8. 红帽Linux6.0镜像文件在哪里下载,Linux(RHEL)5.4/5.5/5.8/6.0/6.3 ISO镜像文件-下载地址...
  9. 如何写好一份专利交底书?
  10. html图片没有白边,css插入背景图片底部有白边的解决方法
  11. iso文件:抱歉,装载文件时出现问题
  12. 读书笔记《深度学习与图像识别原理与实践 大白话讲解对小白易懂》2022-8-5
  13. 带sex的net域名_域名劫持的几种方法、域名劫持有什么方式
  14. 分享招聘工作流程图模板及绘制技巧
  15. android系统怎么刷机教程,如何刷新Android系统? Android手机通用刷机教程
  16. 光遇测试服怎么显示服务器错误,光遇服务器错误怎么办_光遇服务器错误解决方法_3DM手游...
  17. css中white-space的值pre-wrap
  18. [渝粤教育] 宁波财经学院 财务管理 参考 资料
  19. 为什么要使用Spring,为什么要使用控制反转(IOC)和依赖注入(DI),为什么要使用AOP
  20. 计算机在食品上的应用论文,文字设计在食品包装设计中的运用论文

热门文章

  1. 编程语言python入门-手把手教你从零开始用Python语言写爬虫程序
  2. 编程语言python入门-2020年10月编程语言:Java、Python 龙争虎斗
  3. 为何python不好找工作-听说自学Python不好找工作,小白要如何学Python?
  4. python是什么类型的编程语言-Python在编程语言中是什么地位?
  5. python为什么叫爬虫-Python为什么叫爬虫
  6. 自动语音识别的原理是什么,它的作用是什么
  7. 第U题:Java BigDecimal解决··Noder现在上初三了,正在开始复习中考。他每天要计算型如..............
  8. Web API-BOM- 操作浏览器
  9. winform 往第三方传值 put
  10. python中如何导入数据包_如何在python中发送数据包?