STL源码剖析(四)---空间配置器

系统已经提供了动态分配内存的接口,malloc(),new(),为什么需要空间配置器?

空间配置器的存在是为了解决1)容器申请内存时产生的内存碎片问题2)频繁分配小块内存,效率太低

关于内存碎片,举一个例子:

假设系统依次分配了16Byte、8Byte、16Byte、4Byte,还剩余8Byte未分配,后来操作系统回收了上面的两个16byte,总的剩余空间有40byte,这时,操作系统想再分配24Byte,但是却不能分配出一个连续24Byte的空间,这就是内存碎片问题,事实上,这种内存碎片问题叫做外碎片,还有一种内存碎片问题叫做内碎片,后面会说到。

STL空间配置器的框架设计

1)有一级空间配置器和二级空间配置器


2)通过__USE_MALLOC宏配置判断是否使用二级空间配置器


关于内存池:

在二级空间配置器中的核心就是内存池,它管理着自由链表


1)当一个容器需要分配内存时,如果需要的内存大小大于128个字节,就去一级空间配置器,小于就去二级空间配置器。

2)先去自由链表取,如果需要7个字节,就取8Byte的内存块,如果需要9字节就取16Byte的内存块,这也就是内碎片的问题。如果自由链表为空,就调用refill函数,从内存池中取出内存块挂到自由链表。

这两步都是allocate()函数干的事~~~

3)refill()函数会调用chunk_alloc()函数,函数返回20个所需内存块(如果不够,能分配多少分配多少),返回第一个内存块,剩余的挂到自由链表。

4)chunk_alloc()函数负责从内存池中取内存,如果内存池中的内存足够,bytes_left >= total_bytes ,则直接从内存池中取;内存池中的内存不足,但是够一个bytes_left >= size,则直接取能够取出来的;内存池中的内存不足,则从系统堆分配大块内存到内存池中。

源码:

<span style="font-family:Microsoft YaHei;">//</span><span style="color:#ff0000;font-family: 'Microsoft YaHei';">一级空间配置器,用来处理内存不足的情况</span><span style="font-family:Microsoft YaHei;">
template <int inst>
class __malloc_alloc_template {

private:

static void *oom_malloc(size_t);

static void *oom_realloc(void *, size_t);


public:

static void * allocate(size_t n)
{
    void *result = malloc(n);
    if (0 == result) result = oom_malloc(n);
    return result;
}

static void deallocate(void *p, size_t /* n */)
{
    free(p);
}

static void * reallocate(void *p, size_t /* old_sz */, size_t new_sz)
{
    void * result = realloc(p, new_sz);
    if (0 == result) result = oom_realloc(p, new_sz);
    return result;
}

static void (* set_malloc_handler(void (*f)()))()
{
    void (* old)() = __malloc_alloc_oom_handler;
    __malloc_alloc_oom_handler = f;
    return(old);
}

};

// malloc_alloc out-of-memory handling

#ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG
template <int inst>
void (* __malloc_alloc_template<inst>::__malloc_alloc_oom_handler)() = 0;
#endif

template <int inst>
void * __malloc_alloc_template<inst>::oom_malloc(size_t n)
{
    void (* my_malloc_handler)();
    void *result;

    for (;;) {
        my_malloc_handler = __malloc_alloc_oom_handler;
        if (0 == my_malloc_handler) { __THROW_BAD_ALLOC; }
        (*my_malloc_handler)();
        result = malloc(n);
        if (result) return(result);
    }
}

template <int inst>
void * __malloc_alloc_template<inst>::oom_realloc(void *p, size_t n)
{
    void (* my_malloc_handler)();
    void *result;

    for (;;) {
        my_malloc_handler = __malloc_alloc_oom_handler;
        if (0 == my_malloc_handler) { __THROW_BAD_ALLOC; }
        (*my_malloc_handler)();
        result = realloc(p, n);
        if (result) return(result);
    }
}

                ...


# ifdef __USE_MALLOC

typedef malloc_alloc alloc;
typedef malloc_alloc single_client_alloc;

# else


//二级空间配置器
template <bool threads, int inst>
class __default_alloc_template {

private:
 
    ...
    enum {__ALIGN = 8};            //排列间隔
    enum {__MAX_BYTES = 128};     //最大值
    enum {__NFREELISTS = __MAX_BYTES/__ALIGN};   //自由链表大小
 
  static size_t ROUND_UP(size_t bytes) {    //将byte向上调整为8的倍数
        return (((bytes) + __ALIGN-1) & ~(__ALIGN - 1));
  }
__PRIVATE:
  union obj {                 //自由链表的结点
        union obj * free_list_link;
        char client_data[1];    /* The client sees this.        */
  };
      ...
  static  size_t FREELIST_INDEX(size_t bytes) {          //计算要使用自由链表中的哪一个下标的内存块,从1开始算
        return (((bytes) + __ALIGN-1)/__ALIGN - 1);
  }

  
  static void *refill(size_t n);
  ...
  static char *chunk_alloc(size_t size, int &nobjs);

                                   
  static char *start_free;         //内存池的起始位置
  static char *end_free;           //内存池的结束位置
  static size_t heap_size;          //从系统堆分配的总内存

   ...

  public:
    __default_alloc_template() {
	// This assumes the first constructor is called before threads
	// are started.
        if (!__node_allocator_lock_initialized) {
            InitializeCriticalSection(&__node_allocator_lock);
            __node_allocator_lock_initialized = true;
        }
    }
    ...
public:

  static void * allocate(size_t n)
  {
    obj * __VOLATILE * my_free_list;
    obj * __RESTRICT result;

    if (n > (size_t) __MAX_BYTES) {
        return(malloc_alloc::allocate(n));
    }
    my_free_list = free_list + FREELIST_INDEX(n);
  

    result = *my_free_list;
    if (result == 0) {
        void *r = refill(ROUND_UP(n));
        return r;
    }
    *my_free_list = result -> free_list_link;
    return (result);
  };

  //释放空间,不是还给操作系统,而是插到自由链表
  static void deallocate(void *p, size_t n)
  {
    obj *q = (obj *)p;
    obj * __VOLATILE * my_free_list;

    if (n > (size_t) __MAX_BYTES) {
        malloc_alloc::deallocate(p, n);
        return;
    }
    my_free_list = free_list + FREELIST_INDEX(n);
       ...
    q -> free_list_link = *my_free_list;
    *my_free_list = q;
  }

  static void * reallocate(void *p, size_t old_sz, size_t new_sz);

} ;

typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc;
typedef __default_alloc_template<false, 0> single_client_alloc;

char* __default_alloc_template<threads, inst>::chunk_alloc(size_t size, int& nobjs)
{
    char * result;
    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 = size * nobjs;
        result = start_free;
        start_free += total_bytes;
        return(result);
    } else {
        size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);
        // Try to make use of the left-over piece.
        if (bytes_left > 0) {
            obj * __VOLATILE * my_free_list =
                        free_list + FREELIST_INDEX(bytes_left);

            ((obj *)start_free) -> free_list_link = *my_free_list;
            *my_free_list = (obj *)start_free;
        }
        start_free = (char *)malloc(bytes_to_get);
        if (0 == start_free) {
            int i;
            obj * __VOLATILE * my_free_list, *p;
            // Try to make do with what we have.  That can't
            // hurt.  We do not try smaller requests, since that tends
            // to result in disaster on multi-process machines.
            for (i = size; i <= __MAX_BYTES; i += __ALIGN) {
                my_free_list = free_list + FREELIST_INDEX(i);
                p = *my_free_list;
                if (0 != p) {
                    *my_free_list = p -> free_list_link;
                    start_free = (char *)p;
                    end_free = start_free + i;
                    return(chunk_alloc(size, nobjs));
                    // Any leftover piece will eventually make it to the
                    // right free list.
                }
            }
	    end_free = 0;	// In case of exception.
            start_free = (char *)malloc_alloc::allocate(bytes_to_get);
            // This should either throw an
            // exception or remedy the situation.  Thus we assume it
            // succeeded.
        }
        heap_size += bytes_to_get;
        end_free = start_free + bytes_to_get;
        return(chunk_alloc(size, nobjs));
    }
}

template <bool threads, int inst>
void* __default_alloc_template<threads, inst>::refill(size_t n)
{
    int nobjs = 20;
    char * chunk = chunk_alloc(n, nobjs);
    obj * __VOLATILE * my_free_list;
    obj * result;
    obj * current_obj, * next_obj;
    int i;

    if (1 == nobjs) return(chunk);
    my_free_list = free_list + FREELIST_INDEX(n);

      result = (obj *)chunk;
      *my_free_list = next_obj = (obj *)(chunk + n);
      for (i = 1; ; i++) {
        current_obj = next_obj;
        next_obj = (obj *)((char *)next_obj + n);
        if (nobjs - 1 == i) {
            current_obj -> free_list_link = 0;
            break;
        } else {
            current_obj -> free_list_link = next_obj;
        }
      }
    return(result);
}

template <bool threads, int inst>
void*__default_alloc_template<threads, inst>::reallocate(void *p,   size_t old_sz,size_t new_sz)
{
    void * result;
    size_t copy_sz;

    if (old_sz > (size_t) __MAX_BYTES && new_sz > (size_t) __MAX_BYTES) {
        return(realloc(p, new_sz));
    }
    if (ROUND_UP(old_sz) == ROUND_UP(new_sz)) return(p);
    result = allocate(new_sz);
    copy_sz = new_sz > old_sz? old_sz : new_sz;
    memcpy(result, p, copy_sz);
    deallocate(p, old_sz);
    return(result);
}






</span>




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值