内存池(MemoryPool)

from: http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26975042&id=4033505

 c语言中,malloc/calloc/realloc, free; c++语言中new/delete/new[]/delete[] 来分配/释放内存.


     1. 使用c/c++分配函数的问题
         (1)在分配和释放内存时,需要在用户空间与内核空间切换。不断调用new/malloc,需要不断的进行上下文切换,是比较花CPU时间的。
         (2)需要自己释放内存。若忘记释放内存,会导致内存泄漏。(内存泄漏->可用物理内存越来越少->使用硬盘作为虚拟内存->访问硬盘速度很慢)
        (3)频繁的分配/释放内存,会导致内存碎片,从而降低程序的运行效率。

     2. 使用内存池的一些策略:
         (1)在程序启动时,预先申请一块较大的内存块。在需要内存时,从该内存块中划分一块出来。这样可以减少new/malloc时系统调用的次数。
         (2)对使用的请求进行优化。比如我们的程序经常需要1K的内存,那我们就可以预先分配一组1K的内存块。等需要的时候,直接拿来使用。
         (3)当不需要某块内存的时候,先将这块内存放到一个容器中,而不是delete。当程序再次需要内存时,可以直接从容器中查找适合的内存块。

   3. C++实现
       下面代码,用两个链表来实现一个简单的内存池。一个为m_used,为正在被使用的;另一个m_freed,可以提供内存供分配。
点击( 此处 )折叠或打开 buffer.h
  1. #pragma once
  2. #include <stdlib.h>
  3. #include <assert.h>
  4. #include <string.h>
  5. #include <iostream>

  6. struct Entry
  7. {
  8.     Entry* next;
  9.     size_t data_len; //总大小
  10.     size_t data_used; //已经使用的
  11.     unsigned char data[0];
  12. };

  13. // 2个链表,分别是已经使用的空间,和没有使用的空间
  14. class MemoryPool
  15. {
  16. private:
  17.     Entry* m_used; //已经使用的
  18.     Entry* m_freed; //没有使用的
  19. private:
  20.     size_t GetEntrySize(const Entry* en)
  21.     {
  22.         size_t size = -1;

  23.         while(en)
  24.         {
  25.             en = en->next;
  26.             size++;
  27.         }
  28.         return size;
  29.     }
  30. public:
  31.     void print_Size()
  32.     {
  33.         std::cout << "m_used = " << GetEntrySize(m_used) << std::endl;
  34.         std::cout << "m_freed= " << GetEntrySize(m_freed) << std::endl << std::endl;
  35.     }
  36. public:
  37.     MemoryPool();
  38.     ~MemoryPool();

  39.     void* memory_new(size_t size);
  40.     void memory_delete(void* p);
  41. };

点击(此处)折叠或打开  buffer.cpp

  1. #include "buffer.h"
  2. #include <string.h>
  3. #include <stdio.h>
  4. #include <sys/time.h>

  5. MemoryPool::MemoryPool()
  6. {
  7.     // 头节点
  8.     m_used = (Entry*) ::operator new(sizeof(Entry));
  9.     m_freed = (Entry*) ::operator new(sizeof(Entry));

  10.     memset(m_used, 0x00, sizeof(Entry));
  11.     memset(m_freed, 0x00, sizeof(Entry));
  12. }

  13. MemoryPool::~MemoryPool()
  14. {
  15.     Entry* p = m_used;
  16.     while (!= NULL)
  17.     {
  18.         Entry* next = p->next;
  19.         delete[] p;
  20.         p = next;
  21.     }

  22.     p = m_freed;
  23.     while (!= NULL)
  24.     {
  25.         Entry* next = p->next;
  26.         delete[] p;
  27.         p = next;
  28.     }
  29. }

  30. void* MemoryPool::memory_new(size_t size)
  31. {
  32.     Entry* p1 = m_freed;
  33.     Entry* p2 = m_freed->next;
  34.     while (p2 != NULL && p2->data_len < size)
  35.     {
  36.         p1 = p2;
  37.         p2 = p2->next;
  38.     }

  39.     // 有空余的空间可以使用
  40.     if (p2 != NULL)
  41.     {
  42.         p2->data_used = size;
  43.         // 从m_free中去掉该节点
  44.         p1->next = p2->next;

  45.         // 将该节点加入到m_used中
  46.         p2->next = m_used->next;
  47.         m_used->next = p2;

  48.         return p2->data;
  49.     }
  50.     // 需要重新开辟空间
  51.     else
  52.     {
  53.         Entry* newEntry =
  54.          (Entry*) new char[sizeof(Entry) + size * sizeof(char)];
  55.         newEntry->data_len = size;
  56.         newEntry->data_used = size;
  57.         newEntry->next = m_used->next;

  58.         m_used->next = newEntry;
  59.         return (void*) newEntry->data;
  60.     }
  61. }

  62. void MemoryPool::memory_delete(void* p)
  63. {
  64.     Entry* p1 = m_used;
  65.     Entry* p2 = m_used->next;
  66.     while(p2 != NULL)
  67.     {
  68.         int iCal = (unsigned char*)- (unsigned char*)&(p2->data[0]);
  69.         // 找到了该地址
  70.         if(iCal >= 0 && iCal <= (int)p2->data_len)
  71.         {
  72.             // 从m_used中删除.
  73.             p2->data_used = 0;
  74.             p1->next = p2->next;
  75.             //加入到m_freed中.
  76.             p2->next = m_freed->next;
  77.             m_freed->next = p2;
  78.             return;
  79.         }
  80.         else
  81.         {
  82.             p1 = p2;
  83.             p2 = p2->next;
  84.         }
  85.     }
  86. }

点击(此处)折叠或打开  测试代码

  1. // 正确性验证
  2. void MemoryPool_test_1()
  3. {
  4.     MemoryPool pool;
  5.     //
  6.     pool.print_Size();
  7.     char* p1 = (char*) pool.memory_new(100);
  8.     printf("p1[0x%.2x] \n", (unsigned int)p1);
  9.     pool.print_Size();
  10.     char* p2 = (char*) pool.memory_new(200);
  11.     printf("p2[0x%.2x] \n", (unsigned int)p2);
  12.     pool.print_Size();

  13.     memcpy(p1, "hello", 6);
  14.     memcpy(p2, "world!", 7);

  15.     std::cout << p1 << p2 << std::endl;
  16.     pool.memory_delete(p1);
  17.     pool.print_Size();
  18.     pool.memory_delete(p2);
  19.     pool.print_Size();


  20.     char* p3 = (char*) pool.memory_new(10);
  21.     printf("p3[0x%.2x] \n", (unsigned int)p3); // = p2
  22.     pool.print_Size();
  23.     char* p4 = (char*) pool.memory_new(500);
  24.     printf("p4[0x%.2x] \n", (unsigned int)p4);
  25.     pool.print_Size();
  26.     char* p5 = (char*) pool.memory_new(100);
  27.     printf("p5[0x%.2x] \n", (unsigned int)p5); // = p1
  28.     pool.print_Size();
  29. }

  30. // 速度验证
  31. void MemoryPool_test_2()
  32. {
  33. #define CONST_1 5000
  34. #define CONST_2 10
  35.     timeval v1, v2, v3;
  36.     // 用new/delete
  37.     gettimeofday(&v1, NULL);
  38.     for(int i = 0; i < CONST_1; ++i)
  39.     {
  40.         char* p[1000];
  41.         for(int j = 0; j < CONST_2; ++j)
  42.         {
  43.             p[j] = new char[1024];
  44.         }
  45.         for(int j = 0; j < CONST_2; ++j)
  46.         {
  47.             delete [] p[j];
  48.         }
  49.     }
  50.     gettimeofday(&v2, NULL);
  51.     timersub(&v2, &v1, &v3);
  52.     printf("new/delete: [%6d:%6d] \n", v3.tv_sec, v3.tv_usec);

  53.     // 用 MemoryPool
  54.     gettimeofday(&v1, NULL);
  55.     MemoryPool pool;
  56.     for(int i = 0; i < CONST_1; ++i)
  57.     {
  58.         char* p[1000];
  59.         for(int j = 0; j < CONST_2; ++j)
  60.         {
  61.             p[j] = (char*)pool.memory_new(1024);
  62.         }
  63.         //pool.print_Size();
  64.         for(int j = 0; j < CONST_2; ++j)
  65.         {
  66.             pool.memory_delete(p[j]);
  67.         }
  68.         //pool.print_Size();
  69.     }
  70.     gettimeofday(&v2, NULL);
  71.     timersub(&v2, &v1, &v3);
  72.     printf("operator-2: [%6d:%6d] \n", v3.tv_sec, v3.tv_usec);
  73. }

    4. 关于多线程
       系统提供的new/delete,malloc/free,是支持多线程的。
       MemoryPool,需要增加额外的代码来支持多线程。
 
   5. 关于new/delete的重载
       (1) C++提供的原型

点击(此处)折叠或打开

  1. void* operator new(std::size_t) throw (std::bad_alloc);
  2. void* operator new[](std::size_t) throw (std::bad_alloc);
  3. void operator delete(void*) throw();
  4. void operator delete[](void*) throw();
  5. void* operator new(std::size_t, const std::nothrow_t&) throw();
  6. void* operator new[](std::size_t, const std::nothrow_t&) throw();
  7. void operator delete(void*, const std::nothrow_t&) throw();
  8. void operator delete[](void*, const std::nothrow_t&) throw();

  9. // Default placement versions of operator new.
  10. inline void* operator new(std::size_t, void* __p) throw() { return __p; }
  11. inline void* operator new[](std::size_t, void* __p) throw() { return __p; }

  12. // Default placement versions of operator delete.
  13. inline void operator delete (void*, void*) throw() { }
  14. inline void operator delete[](void*, void*) throw() { }
       (2) operator new/delete重载,只能够重载成:(A)全局函数,但不能是static型的全局函数,以及有命名空间的函数;(B)C++的static成员函数。
       (3)  operator new,必须有个size_t类型的参数作为第一个参数,后面参数的个数不定. 如void* operator new( size_t p1 , ...);
                 void*  operator new (size_t size){
                                 (void) size;
                                 return malloc(size);
                 }
            更多operator new/delete重载信息,请参考( http://book.51cto.com/art/201202/317799.htm
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值