• 内存管理器(内存池)


    讨论QQ群:135202158

    在一些应用场合,比如一个繁忙的服务器程序中,服务器频繁地接受并处理用户请求,如果每次处理都需要从系统动态地申请和释放内存,由于此操作开销较大,会加大处理时延;另一方面,此开销造成的累积效应也是很大的。

    对此我们可以举一个生活中的例子:假设一个烟鬼一天抽10包烟(这有点太多了,假设啊),如果他每次去商店只买一包烟,他一天就要往商店跑10次,很麻烦;如果他一次买一条(10 包)放着,那么他一天之内就可以不用再跑商店了,想抽烟的时候,直接拿抽剩下的就行了。

    这种预先分配所有所需资源、在运行时直接调配的思想,都可以称为池化策略,比如内存池和线程池。其实上面烟鬼买烟的例子,举的并不是特别恰当。因为池中的资源(内存或线程),在使用完之后,是可以回收的,而烟抽完就没有了。

    这篇随笔主要讲内存池,线程池见http://www.cnblogs.com/zzqcn/p/3625452.html

    要实现内存池,除了预先分配一块大内存块之外,我们还需要设计一个用来记录当前内存使用情况的数据结构,它至少需要记录以下信息:

    1. 某个内存块是否可用
    2. 内存块的起始地址
    3. 内存块的大小

    我们可以设计一个双向循环链表来保存内存使用信息,而表中每一个结点如下图所示:

    如果用C++语言表示,则如下(mbl:memory block list):

    struct mblnode
    {
        mblnode*    left;
        mblnode*    right;
        bool        tag;
        size_t      offset;
        size_t      size;
    };

    另外,还需要制订内存分配和回收的策略。这些策略包括:

    1. 从哪个地方开始分配
    2. 用户申请多少,就分配多少,还是有一个分配最小值
    3. 回收某个内存块之后,如何和其他空闲块合并成大的空闲块
    4. 回收内存块之后,下次分配的位置是否变化

    讨论分配和策略之前,我们先设定一个当前指针p,它指向一个可用块,每次内存分配都从这个可用块进行。

    对于1:每次分配后,都将p指向已分配块的下一个可用内存块;
    对于2:设定一个最小值MINSIZE,当用户申请的内存块大小小于MINSIZE时,就分配MINSIZE大小的内存块给用户;
    对于3:回收某块内存后,需要根据多种不同情况,与周围的可用块进行合并,形成更大的可用块;
    对于4:回收内存后,应比较新合并的可用块的大小和p指向的内存块的大小,并将p指向更大的内存块。

    上述分配回收策略,都减小了内存池中的碎片,提升分配效率。

    下面用图演示一下分配过程。假设内存池大小为12,绿色代表空闲块,红色代表已用块,p为当前分配指针,下面的数据结构是我们的双向循环链表,链表结点中第1项是是否可用的标志,打勾表示可用,打x表示已用,第2项表示内存块起始地址,第3项表示此块的大小。下图表示从完全空闲的内存池连续分配大小为2、5的两块内存的情况:

     下图表示回收一块内存时与周围空闲块合并的多种情况,每一种情况都需要小心地编码:

    完整C++代码:

      1 /*
      2  * 内存池实现 - 使用双循环链表.
      3  * Author: 赵子清
      4  * Blog: http://www.cnblogs.com/zzqcn
      5  **/
      6 
      7 
      8 #include <cstdlib>
      9 #include <cstdio>
     10 
     11 
     12 struct mblnode
     13 {
     14     mblnode*    left;
     15     mblnode*    right;
     16     bool        tag;
     17     size_t      offset;
     18     size_t      size;
     19 };
     20 
     21 
     22 #define     MAXBUFF     12
     23 #define     MINSIZE     2
     24 
     25 char        BUFF[MAXBUFF];
     26 mblnode*    mbl = NULL;
     27 
     28 int     my_init();
     29 int     my_deinit();
     30 char*   my_new(size_t _n);
     31 int     my_del(char* _p);
     32 void    my_merge_nil  (mblnode* _p);
     33 void    my_merge_left (mblnode* _p);
     34 void    my_merge_right(mblnode* _p);
     35 void    my_merge_both (mblnode* _p);
     36 int     my_print_mbl();
     37 
     38 
     39 int  main(int argc, char** argv)
     40 {
     41     my_init();
     42 
     43     // new
     44     char* p = my_new(2);
     45     if (p != NULL)
     46     {
     47         p[0] = 'a';
     48         p[1] = 'b';
     49     }
     50     my_print_mbl();
     51 
     52     // new
     53     char* p2 = my_new(5);
     54     if (p2 != NULL)
     55     {
     56         p2[0] = 'c';
     57         p2[1] = 'd';
     58         p2[2] = 'e';
     59         p2[3] = 'f';
     60         p2[4] = 'g';
     61     }
     62     my_print_mbl();
     63 
     64     // new
     65     char* p3 = my_new(2);
     66     if (p3 != NULL)
     67     {
     68         p3[0] = 'h';
     69         p3[1] = 'i';
     70     }
     71     my_print_mbl();
     72 
     73     my_del(p2);     my_print_mbl();
     74     my_del(p);      my_print_mbl();
     75     my_del(p3);     my_print_mbl();
     76 
     77     my_deinit();
     78     
     79     system("PAUSE");
     80     return 0;
     81 }
     82 
     83 
     84 int  my_init()
     85 {
     86     if (mbl != NULL)
     87         my_deinit();
     88 
     89     mblnode* p = new mblnode;
     90     p->left = p->right = p;
     91     p->offset = 0;
     92     p->size = MAXBUFF;
     93     p->tag = false;
     94 
     95     mbl = p;
     96 
     97     return 0;
     98 }
     99 
    100 
    101 int  my_deinit()
    102 {
    103     if (NULL == mbl)
    104         return 0;
    105 
    106     mblnode*  p = NULL;
    107     mblnode*  q = NULL;
    108 
    109     p = mbl;
    110     do
    111     {
    112         q = p;
    113         p = p->right;
    114         delete q;   q = NULL;
    115     } while (p != mbl);
    116 
    117     return 0;
    118 }
    119 
    120 
    121 char*  my_new(size_t _n)
    122 {
    123     mblnode*  p = NULL;
    124     char*     ret = NULL;
    125 
    126     if (NULL == mbl)
    127         return NULL;
    128 
    129     if (_n < MINSIZE)
    130         _n = MINSIZE;
    131 
    132     for (p = mbl; p->right != mbl; p = p->right)
    133     {
    134         if (p->tag || p->size < _n)
    135             continue;
    136         else
    137             break;
    138     }
    139 
    140     if (NULL == p || p->size < _n)
    141         return NULL;
    142 
    143     if (p->size > _n)
    144     {
    145         mblnode*  pnew = new mblnode;
    146         p->left->right = pnew;
    147         pnew->left  = p->left;
    148         pnew->right = p;
    149         pnew->offset = p->offset;
    150         pnew->size = _n;
    151         pnew->tag = true;
    152 
    153         p->size -= _n;
    154         p->offset += _n;
    155         p->left = pnew;
    156 
    157         mbl = p;
    158         ret = BUFF + pnew->offset;
    159     }
    160     else if (p->size == _n)
    161     {
    162         p->tag = true;
    163         
    164         mbl = p;
    165         ret = BUFF + p->offset;
    166     }
    167 
    168     return ret;
    169 }
    170 
    171 
    172 int  my_del(char* _p)
    173 {
    174     if (NULL == _p || _p < BUFF || _p >= BUFF + MAXBUFF)
    175         return -1;
    176 
    177     int  ret = -1;
    178     mblnode*  p = NULL;
    179     mblnode*  pcur = NULL;
    180 
    181     p = mbl;
    182     do
    183     {
    184         if ((p->offset + BUFF) == _p)
    185         {
    186             pcur = p;
    187             break;
    188         }
    189         p = p->right;
    190     } while (p != mbl);
    191    
    192 
    193     if (NULL == pcur)
    194         return -1;
    195 
    196     /* left & right are all NOT free */
    197     if (pcur->left->tag && pcur->right->tag)
    198     {
    199         my_merge_nil(pcur);
    200         ret = 0;
    201     }
    202     /* left is free */
    203     else if (!pcur->left->tag && pcur->right->tag)
    204     {
    205         if (pcur->offset != 0)
    206             my_merge_left(pcur);
    207         /* upper boundary */
    208         else
    209             my_merge_nil(pcur);
    210 
    211         ret = 0;
    212     }
    213     /* right is free */
    214     else if (pcur->left->tag && !pcur->right->tag)
    215     {
    216         if (pcur->right->offset != 0)
    217             my_merge_right(pcur);
    218         /* lower boundary */
    219         else
    220             my_merge_nil(pcur);
    221 
    222         ret = 0;
    223     }
    224     /* left and right are all free */
    225     else if (!pcur->left->tag && !pcur->right->tag)
    226     {
    227         //if (0 == pcur->left->offset)
    228         if (0 == pcur->offset)
    229             my_merge_right(pcur);
    230         else if (0 == pcur->right->offset)
    231             my_merge_left(pcur);
    232         else
    233             my_merge_both(pcur);
    234         
    235         ret = 0;
    236     }
    237 
    238     return ret;
    239 }
    240 
    241 
    242 void  my_merge_nil(mblnode* _p)
    243 {
    244     _p->tag = false;
    245     if (mbl->size < _p->size)
    246         mbl = _p;
    247 }
    248 
    249 void  my_merge_left(mblnode* _p)
    250 {
    251     _p->left->right = _p->right;
    252     _p->right->left = _p->left;
    253     _p->left->size += _p->size;
    254 
    255     if (mbl->size < _p->left->size)
    256         mbl = _p->left;
    257 
    258     delete _p;    _p = NULL;
    259 }
    260 
    261 void  my_merge_right(mblnode* _p)
    262 {
    263     _p->left->right = _p->right;
    264     _p->right->left = _p->left;
    265     _p->right->offset = _p->offset;
    266     _p->right->size += _p->size;
    267 
    268     if (mbl->size < _p->right->size)
    269         mbl = _p->right;
    270 
    271     delete _p;    _p = NULL;
    272 }
    273 
    274 void  my_merge_both(mblnode* _p)
    275 {
    276     _p->left->right = _p->right->right;
    277     _p->right->right->left = _p->left;
    278 
    279     _p->left->size += _p->size + _p->right->size;
    280 
    281     if (mbl->size < _p->left->size)
    282         mbl = _p->left;
    283 
    284     delete _p->right;    _p->right = NULL;
    285     delete _p;    _p = NULL;
    286 }
    287 
    288 
    289 int  my_print_mbl()
    290 {
    291     if (NULL == mbl)
    292         return -1;
    293 
    294     mblnode* p = mbl;
    295 
    296     do
    297     {
    298         printf("[%c, %d, %d]
    ",
    299                 p->tag ? 'x' : 'v',
    300                 p->offset,
    301                 p->size);
    302         p = p->right;
    303     } while (p != mbl);
    304 
    305     printf("------------------
    ");
    306 
    307     return 0;
    308 }

    输出结果:

    [v, 2, 10]
    [x, 0, 2]
    ------------------
    [v, 7, 5]
    [x, 0, 2]
    [x, 2, 5]
    ------------------
    [v, 9, 3]
    [x, 0, 2]
    [x, 2, 5]
    [x, 7, 2]
    ------------------
    [v, 2, 5]
    [x, 7, 2]
    [v, 9, 3]
    [x, 0, 2]
    ------------------
    [v, 0, 7]
    [x, 7, 2]
    [v, 9, 3]
    ------------------
    [v, 0, 12]
    ------------------
    请按任意键继续. . .

    【参考资料】

    《数据结构(C语言版)》,严蔚敏 吴伟民 编著,清华大学出版社。

  • 相关阅读:
    catalina.sh详解
    jenkins环境变量问题
    张量或维度表示数学理解思路
    YOLO v3重点理解、单元格坐标系、偏移量以及放缩、置信度
    YOLO v3重点理解、单元格坐标系、偏移量以及放缩、置信度
    yolo v3好的想法和一些很好的见解
    损失函数的选择,交叉熵函数的分类以及为什么使用这种损失函数可以提升效果,为什么划分格子grid大小最后是变化的,不是固定的。
    多维python切片,和yolo最后结构1,3,16,16,85的理解
    进度条
    .argmax(-1)理解
  • 原文地址:https://www.cnblogs.com/zzqcn/p/3585003.html
Copyright © 2020-2023  润新知