定长内存池计划核心:如何用固定块内存实现琐屑片管理 ...

打印 上一主题 下一主题

主题 1837|帖子 1837|积分 5511

一、池化技术

        所谓“池化技术”,就是程序先向系统申请过量的资源,然后⾃⼰管理,以备不时之需。之所以要申请过量的资源,是因为每次申请该资源都有较⼤的开销,不如提前申请好了,这样使⽤时就会变得⾮常快捷,⼤⼤提⾼程序运⾏效率。
        在盘算机中,有很多使⽤“池”这种技术的地⽅,除了内存池,另有毗连池、线程池、对象池等。以服务器上的线程池为例,它的主要思想是:先启动若⼲数量的线程,让它们处于就寝状态,当接收到客户端的请求时,唤醒池中某个就寝的线程,让它来处理客户端的请求,当处理完这个请求,线程⼜进⼊就寝状态。
二、内存池

       内存池是指程序预先从利用系统申请⼀块⾜够⼤的内存,此后,当程序中必要申请内存的时候,不是直接向利用系统申请,⽽是直接从内存池中获取;同理,当程序释放内存的时候,并不真正将内存返回给利用系统,⽽是返回内存池。当程序退出(大概特定时间)时,内存池才将之前申请的内存真正释放。
三、内存池主要解决的题目

        内存池主要解决的固然是效率的题目,其次假如作为系统的内存分配器的⻆度,还必要解决⼀下内存碎⽚的题目。那么什么是内存碎⽚呢?
        内存碎⽚分为外碎⽚和内碎⽚,外部碎⽚是⼀些空闲的连续内存区域太⼩,这些内存空间不连续,以⾄于合计的内存⾜够,但是不能满⾜⼀些的内存分配申请需求。内部碎⽚是由于⼀些对⻬的需求,导致分配出去的空间中⼀些内存⽆法被利⽤。
        malloc申请内存能顺应于大多数场景,是通用的,但在某些特定场景下效率并不高,而且大概会产生大量的内存碎片。
四、定长内存池的实现

1.定长内存池的原理


        如上所述,我们先向系统申请一大块空间,然后当我们必要申请内存时就从这块空间上获取,我们把空间用完后不消急着释放回系统,而是把它用一个自由链表毗连起来,举行二次利用。也就是说我们再次申请空间时可以在这块废弃的空间上找,假如没有再去大空间上找,假如还不敷再行止系统申请。
2.框架

        封装一个类,它的成员变量应包罗:一个size_t范例储存该块空间的字节大小一个void*指针储存自由链表的头char*指针指向这块空间的起始地址,char*范例指针每加1只向后移动一字节,方便指针的移动。因为我们每被用户申请一小块空间后,指针往后移动到下一个为没利用的空间的起始地址。
  1. template <typename T>
  2. class FixedMemoryPool
  3. {
  4. public:
  5.     T *New();
  6.     void Delete(T *p);
  7. private:
  8.     size_t _sumSize = 0;
  9.     char *_memory = nullptr;
  10.     void *_list_head = nullptr;
  11. };
复制代码
        初始化利用只必要在成员变量声明这里举行,然后使用默认的构造函数就可以,主要必要我们来实现New和Delete的逻辑。
3.Delete实现

        Delete主要就是来维护自由链表,我们先来实现自由链表,因为在New中必要先在自由链表中查找空间。首先执行它的析构清空在它身上申请的空间。因为头插比较方便,我们直接把它头插到自由链表中。接下来就是头插利用:
        我们把这块必要插入到链表的废弃空间记为p,首先把_list_head储存到p空间的前4/8字节,因为并不确定用户用的是32位系统照旧64位系统,所以可以用这样一个利用来解决:
*(void **)p = _list_head;
然后_list_head = p,如下:
  1. void Delete(T *p)
  2. {
  3.     p->~T();
  4.     *(void **)p = _list_head;
  5.     _list_head = p;
  6. }
复制代码
4.New实现

        我们先来界说一个T* ret用来储存返回值,然后先去自由链表里找空间,如:
  1. if (_list_head != nullptr)
  2. {
  3.     ret = (T *)_list_head;
  4.     _list_head = *(void **)_list_head;
  5. }
复制代码
        大家看到 _list_head = *(void **)_list_head 这个代码大概会一点懵我来逐一讲解以下:
        _list_head是一个void*指针,储存了一个地址,而这个地址空间里面又储存了下一个节点的指针,(void**)相当于告诉编译器我是指向一个void*范例的指针,然后举行解引用得到这块地址,并更新_list_head。
假如链表里没有空间向大空间里获取:
  1. else
  2. {
  3.     int objSize = max(sizeof(T), sizeof(void *));
  4.     if (_sumSize < objSize)//空间不够向系统申请
  5.     {
  6.         _memory = (char *)malloc(128 * 1024);
  7.         _sumSize = 128 * 1024;
  8.     }
  9.     ret = (T *)_memory;
  10.     _memory += objSize;//移向未被利用的起始空间
  11.     _sumSize -= objSize;//更新剩余空间的大小
  12. }
复制代码
objSize的作用:
        因为我们要保证这块空间至少要能存放得下一个地址,要否则被弃用后无法毗连到自由链表。
        注意这里_sumSize不能是+=128*1024,因为_memory的初始地址已经更新了,要与_memory匹配。
最后对ret使用定位 new 后返回即可。
5.性能测试

源码已放在下文,如下是测试结果:

我们可以看到用定长内存池比new申请内存要快得多得多。
注:new的底层调用的就是malloc。
五、源码

FixedMemoryPool.h

  1. #include <iostream>
  2. #include <string>
  3. #include <algorithm>
  4. #include <memory>
  5. using namespace std;
  6. namespace my_MemoryPool
  7. {
  8.     template <typename T>
  9.     class FixedMemoryPool
  10.     {
  11.     public:
  12.         T *New()
  13.         {
  14.             T *ret = nullptr;
  15.             if (_list_head != nullptr)
  16.             {
  17.                 ret = (T *)_list_head;
  18.                 _list_head = *(void **)_list_head;
  19.             }
  20.             else
  21.             {
  22.                 int objSize = max(sizeof(T), sizeof(void *));
  23.                 if (_sumSize < objSize)
  24.                 {
  25.                     _memory = (char *)malloc(128 * 1024);
  26.                     _sumSize = 128 * 1024;
  27.                 }
  28.                 ret = (T *)_memory;
  29.                 _memory += objSize;
  30.                 _sumSize -= objSize;
  31.             }
  32.             new (ret) T;
  33.             return ret;
  34.         }
  35.         void Delete(T *p)
  36.         {
  37.             p->~T();
  38.             *(void **)p = _list_head;
  39.             _list_head = p;
  40.         }
  41.     private:
  42.         size_t _sumSize = 0;
  43.         char *_memory = nullptr;
  44.         void *_list_head = nullptr;
  45.     };
  46. }
复制代码
test.cc

  1. #include "FixedMemoryPool.h"
  2. #include <vector>
  3. using namespace my_MemoryPool;
  4. struct TreeNode
  5. {
  6.         int _val;
  7.         TreeNode* _left;
  8.         TreeNode* _right;
  9.         TreeNode()
  10.                 :_val(0)
  11.                 , _left(nullptr)
  12.                 , _right(nullptr)
  13.         {}
  14. };
  15. void TestObjectPool()
  16. {
  17.         // 申请释放的轮次
  18.         const size_t Rounds = 5;
  19.         // 每轮申请释放多少次
  20.         const size_t N = 10000;
  21.         std::vector<TreeNode*> v1;
  22.         v1.reserve(N);
  23.         size_t begin1 = clock();
  24.         for (size_t j = 0; j < Rounds; ++j)
  25.         {
  26.                 for (int i = 0; i < N; ++i)
  27.                         v1.push_back(new TreeNode);
  28.                 for (int i = 0; i < N; ++i)
  29.                         delete v1[i];
  30.                 v1.clear();
  31.         }
  32.         size_t end1 = clock();
  33.         std::vector<TreeNode*> v2;
  34.         v2.reserve(N);
  35.         FixedMemoryPool<TreeNode> TNPool;
  36.         size_t begin2 = clock();
  37.         for (size_t j = 0; j < Rounds; ++j)
  38.         {
  39.                 for (int i = 0; i < N; ++i)
  40.                         v2.push_back(TNPool.New());
  41.                 for (int i = 0; i < N; ++i)
  42.                         TNPool.Delete(v2[i]);
  43.                 v2.clear();
  44.         }
  45.         size_t end2 = clock();
  46.         cout << "new cost time:" << end1 - begin1 << endl;
  47.         cout << "object pool cost time:" << end2 - begin2 << endl;
  48. }
  49. int main()
  50. {
  51.     TestObjectPool();
  52.     return 0;
  53. }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
继续阅读请点击广告

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

魏晓东

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表