zoukankan      html  css  js  c++  java
  • stl中的空间配置器

     一般我们习惯的c++内存配置如下

    class Foo { ... };
    Foo* pf = new Foo; 
    delete pf; 

     这里的new实际上分为两部分执行。首先是先用::operator new配置内存,然后执行Foo::Foo()构造对象内容。delete也一样,先运行Foo::~Foo()析构对象,再用::operator delete释放内存。在SGI STL中,这两部分分别在<stl_alloc.h>和<stl_construct.h>中。本文讲的便是<stl_alloc.h>中的故事。
      SGI STL中将配置器分为两级。第一级直接用malloc和free管理内存,第二级则使用内存池以避免内存碎片。这两级都由simple_alloc包装起来以符合stl标准。如图

    第一级由于没有用operator new,所以要自己实现new-handler机制。我仿写的代码如下

     1 #ifndef _MALLOC_ALLOC_H_ 
     2 #define _MALLOC_ALLOC_H_ 
     3 
     4 //定义内存不足又没有定义相关处理函数时抛出的异常
     5 #ifndef THROW_OOM
     6 #    include <stdio.h>
     7 #    include <stdlib.h>
     8 #    define THROW_OOM fprintf(stderr, "out of memory
    "); exit(1)
     9 #endif
    10 
    11 #include<stdlib.h>
    12 
    13 namespace Chenstl{
    14 
    15 //第一级空间配置器,直接用mallloc分配内存
    16 //当需要分配的空间大于MAX_BYTES时使用
    17     class malloc_alloc{
    18     private:
    19         static void *oom_malloc(size_t);    //声明时可以只写类型啊。。现在才知道
    20         static void *oom_realloc(void *,size_t);
    21         static void (* malloc_oom_handler)();    //处理malloc时内存不足时的函数指针
    22     public:
    23         static void *allocate(size_t n);
    24         static void decllocate(void *p);
    25 
    26         static void *realloc(void *p, size_t new_sz);
    27         //当内存不足时,需要客户端设置handler
    28         static void set_malloc_oom_handler(void(*f)());
    29     };    
    30 }
    31 
    32 #endif
     1 #include "malloc_alloc.h"
     2 
     3 using namespace Chenstl;
     4 void *malloc_alloc::allocate(size_t n)
     5 {
     6     void *result = malloc(n);
     7     if (0 == result) result = oom_malloc(n);
     8     return result;
     9 }
    10 
    11 void malloc_alloc::decllocate(void *p)
    12 {
    13     free(p);
    14 }
    15 
    16 void * malloc_alloc::realloc(void *p, size_t new_sz)
    17 {
    18     void *result = realloc(p, new_sz);
    19     if (0 == result)    result = oom_realloc(p, new_sz);
    20     return result;
    21 }
    22 
    23 //当内存不足时,需要客户端设置handler
    24 void malloc_alloc::set_malloc_oom_handler(void(*f)())
    25 {
    26     malloc_oom_handler = f;
    27 }
    28 
    29 void(*malloc_alloc::malloc_oom_handler)() = 0;
    30 
    31 void *malloc_alloc::oom_malloc(size_t n)
    32 {//不断试图获得内存
    33     void *result;
    34     for (;;)    //据说这样比while(1)效果更优
    35     {
    36         if (0 == malloc_oom_handler) THROW_OOM;
    37         (*malloc_oom_handler)();
    38         result = malloc(n);
    39         if (result)    return result;
    40     }
    41 }
    42 
    43 void *malloc_alloc::oom_realloc(void *p, size_t n)
    44 {
    45     void *result;
    46     for (;;)
    47     {
    48         if (0 == malloc_oom_handler) THROW_OOM;
    49         (*malloc_oom_handler)();
    50         result = realloc(p, n);
    51         if (result)    return result;
    52     }
    53 }
    malloc_alloc.cpp

      如果需要的区块超过128bytes则用第一级,否则用第二级的内存池管理。为了便于管理,配置器会自动将内存需求量上调到8的倍数(要求20bytes时,自动调整为24bytes)。用16个freelist管理内存池,为节省空间,使用union

    union obj {   //free-lists的节点构造 
       union obj *next;
       char client[1]; //使用者可见
      };

     获取内存时的代码及步骤如下

    void *default_alloc::allocate(size_t n)
    {
        obj *result = 0;
        obj **my_free_list = 0;
        if (n > MAX_BYTES)
            return malloc_alloc::allocate(n);
        //寻找free lists中合适的一个                
        my_free_list = free_list + FREELIST_INDEX(n);
        result = *my_free_list;
        if(0 == result)
        {//没有找到可用的freelist,从内存池里取出空间
            return refill(ROUND_UP(n));
        }
        //调整freelist
        *my_free_list = result->next;
        return result;
    }
    View Code

     

      当free list中没有可用区块时,调用refill()为free list填充空间,新的空间取自内存池(由chunk_alloc()完成)。如果内存池不够,则malloc之,如果系统heap空间也不够,chunk_alloc()就寻找还有空闲区块的free list并将其内存充公,如果还是不够就调用第一级配置器。第一级配置器有实现new-handler机制,内存不够会抛出异常。

     
    #ifndef _DEFAULT_ALLOC_H
    #define _DEFAULT_ALLOC_H
    
    namespace Chenstl{    
        //使用内存池以减少碎片
        class default_alloc {
        private:
            enum { ALIGN = 8};
            enum { MAX_BYTES = 128 };
            enum { NFREELISTS = 16 };
            //static const int ALIGN = 8;
            //static const int MAX_BYTES = 128;
            //static const int NFREELISTS = 16;    //MAX_BYTES/ALIGN
            union obj {            //free-lists的节点构造 
                union obj *next;
                char client[1];
            };
            //freelist
            static obj *free_list[NFREELISTS];
            static char *start_free;    //内存池的起始位置
            static char *end_free;        //内存池的终止位置
            static size_t heap_size;
    
        private:
            //将bytes上调至8的倍数 
            static size_t ROUND_UP(size_t bytes) {
                return ((bytes +ALIGN - 1) & ~(ALIGN - 1));
            }
            //获取合适的区块在freelist中的位置
            static  size_t FREELIST_INDEX(size_t __bytes) {
                return (((__bytes)+(size_t)ALIGN - 1) / (size_t)ALIGN - 1);
            }
            //返回一个大小为n的对象,并可能加入大小为n的其他区块到free-list
            static void *refill(size_t n);
            //配置一大块空间,可容纳nobjs个大小为size的区块
            //如果配置nobjs个区块有所不便,nobjs可能会降低
            static char *chunk_alloc(size_t size, int &nobjs);
    
        public:
            static void *allocate(size_t n);
            static void deallocate(void *p, size_t n);
            static void *realloc(void *p, size_t old_sz, size_t new_sz);
        };
    }
    
    #endif
    default_alloc.h
    #include "default_alloc.h"
    #include "malloc_alloc.h"
    using namespace Chenstl;
    
    default_alloc::obj *default_alloc::free_list[NFREELISTS]
    = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
    char *default_alloc::start_free = 0;    //内存池的起始位置
    char *default_alloc::end_free = 0;        //内存池的终止位置
    size_t default_alloc::heap_size = 0;
    
    void *default_alloc::allocate(size_t n)
    {
        obj *result = 0;
        obj **my_free_list = 0;
        if (n > MAX_BYTES)
            return malloc_alloc::allocate(n);
        //寻找free lists中合适的一个                
        my_free_list = free_list + FREELIST_INDEX(n);
        result = *my_free_list;
        if(0 == result)
        {//没有找到可用的freelist,从内存池里取出空间
            return refill(ROUND_UP(n));
        }
        //调整freelist
        *my_free_list = result->next;
        return result;
    }
    
    void default_alloc::deallocate(void *p, size_t n)
    {
    
    }
    //返回一个大小为n的对象,并可能加入大小为n的其他区块到freelist
    //在ANSI c中,void *不允许进行加减操作,所以chunk用char *
    void *default_alloc::refill(size_t n)
    {
        int objs = 20;
        char *chunk = chunk_alloc(n, objs);
        
        obj *next, *current;
        obj *result;
        obj **my_free_list;
        if (1 == objs)    //只取出一个区块
            return chunk;
        my_free_list = free_list + FREELIST_INDEX(n);
        result = (obj *)chunk;    //这一块返回给客户端
        //将freellist指向分配的区域
        *my_free_list = next = (obj *)chunk + n;
        for (int i = 1;; i++)
        {
            current = next;
            next = (obj *)((char *)next + n);    //这里注意不能直接用next+n
            if (i == objs - 1)
            {
                current->next = 0;
                break;
            }
            else
                current->next = next;
        }
        return result;    
    }
    
    char *default_alloc::chunk_alloc(size_t size, int &nobjs)
    {
        char *result = 0;
        size_t total_bytes = size*nobjs;
        size_t bytes_left = end_free - start_free;    //内存池剩余空间
        if (bytes_left >= total_bytes)
        {//内存池足够提供所需内存
            result = start_free;
            start_free += total_bytes;
            return result;
        }
        else if (bytes_left >= size)
        {//内存池足够供应一个以上的区块
            nobjs = bytes_left / size;
            total_bytes = nobjs * size;
            result = start_free;
            start_free += total_bytes;
            return result;
        }
        else
        {//内存池一块区块也供应不了
            size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);;
            if (bytes_left>0)
            {//将内存池的零头分配给合适的freelist
                obj **my_free_list = free_list + FREELIST_INDEX(bytes_left);
                ((obj *)start_free)->next = *my_free_list;
                *my_free_list = (obj *)start_free;
            }
            start_free = (char *)malloc(bytes_to_get);
            if (!start_free)
            {//系统堆内存不足,寻找还未使用的freelist
                obj *p = 0;
                obj **my_free_list = 0;
                for (int i = size; i < MAX_BYTES; ++i)
                {
                    my_free_list = free_list + FREELIST_INDEX(i);
                    p = *my_free_list;
                    if (0 != p)
                    {//还有未使用的freelist
                        start_free = (char *)p;
                        *my_free_list = p->next;
                        end_free = start_free + i;
                        //递归调用,修正nobjs
                        return chunk_alloc(size, nobjs);
                    }
                }
                //没内存可用,寄希望于第一级的new-handler或抛出异常
                end_free = 0;
                start_free = (char *)malloc_alloc::allocate(bytes_to_get);
            }
            heap_size += bytes_to_get;
            end_free = start_free + bytes_to_get;
            return chunk_alloc(size, nobjs);//递归调用,修正nobjs
        }
    }
    default_alloc.cpp
  • 相关阅读:
    统计创建对象个数
    动手动脑
    开学第一周心得
    放假第五周总结
    放假第四周总结
    第一周总结
    04-异常处理-动手动脑
    03-继承与多态-动手动脑
    02-类和对象-跟踪类对象创建个数
    02-类和对象-动手动脑
  • 原文地址:https://www.cnblogs.com/tonychen-tobeTopCoder/p/5152572.html
Copyright © 2011-2022 走看看