STL之内存管理

来源:互联网 发布:java supplier 编辑:程序博客网 时间:2024/06/14 00:18

STL的内存管理分为两级

第一个级别用于较大内存分配与释放的管理(>128byte), malloc_alloc

第二个级别用于小于128byte的内存管理,default_alloc


1 mallloc_alloc实现


它直接采用c语言中的malloc, realloc和free实现

源码如下

<span style="font-size:14px;">template <int __inst>class __malloc_alloc_template {private:  static void* _S_oom_malloc(size_t);  static void* _S_oom_realloc(void*, size_t);#ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG  static void (* __malloc_alloc_oom_handler)();#endifpublic:  static void* allocate(size_t __n)  {    void* __result = malloc(__n);    if (0 == __result) __result = _S_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 = _S_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);  }};</span>

2 default_alloc实现

这是系统默认的内存管理器


原理:

它内部维护着一个大小为16的free_list数组,用于管理8, 16, 24, ..., 128共16个不同大小的空间.

相同大小的空间都以链表方式组织.这部分空间故且称之为"自由空间".

同时,它还维护着一个"堆空间",通过以下变量

static char* _S_start_free;//堆空间开始位置
  static char* _S_end_free;//结束位置
  static size_t _S_heap_size;//大小

模板源码:

<span style="font-size:14px;">template <bool threads, int inst>class __default_alloc_template {private:  // Really we should use static const int x = N  // instead of enum { x = N }, but few compilers accept the former.# ifndef __SUNPRO_CC    enum {_ALIGN = 8};    enum {_MAX_BYTES = 128};    enum {_NFREELISTS = 16}; // _MAX_BYTES/_ALIGN# endif  static size_t  _S_round_up(size_t __bytes)     { return (((__bytes) + (size_t) _ALIGN-1) & ~((size_t) _ALIGN - 1)); }__PRIVATE:  union _Obj {        union _Obj* _M_free_list_link;        char _M_client_data[1];    /* The client sees this.        */  };private:# ifdef __SUNPRO_CC    static _Obj* __STL_VOLATILE _S_free_list[];         // Specifying a size results in duplicate def for 4.1# else    static _Obj* __STL_VOLATILE _S_free_list[_NFREELISTS]; # endif  static  size_t _S_freelist_index(size_t __bytes) {//从0开始        return (((__bytes) + (size_t)_ALIGN-1)/(size_t)_ALIGN - 1);  }  // Returns an object of size __n, and optionally adds to size __n free list.  static void* _S_refill(size_t __n);  // Allocates a chunk for nobjs of size size.  nobjs may be reduced  // if it is inconvenient to allocate the requested number.  static char* _S_chunk_alloc(size_t __size, int& __nobjs);  // Chunk allocation state.  static char* _S_start_free;  static char* _S_end_free;  static size_t _S_heap_size;# ifdef __STL_THREADS    static _STL_mutex_lock _S_node_allocator_lock;# endif    // It would be nice to use _STL_auto_lock here.  But we    // don't need the NULL check.  And we do need a test whether    // threads have actually been started.    class _Lock;    friend class _Lock;    class _Lock {        public:            _Lock() { __NODE_ALLOCATOR_LOCK; }            ~_Lock() { __NODE_ALLOCATOR_UNLOCK; }    };public:/** * 1) n > 128-------> 一级配置器 malloc * 2) 获取n对应的free_list槽位,不是空分配一个并调整剩余空间 * 3) 若为空,调整n到8的整数倍, 调用refill获取新的空间, * 如果refill返回保证会有可用空间,为上层提供可靠服务 */  /* __n must be > 0      */  static void* allocate(size_t __n)  {    void* __ret = 0;    if (__n > (size_t) _MAX_BYTES) {      __ret = malloc_alloc::allocate(__n);    }    else {      _Obj* __STL_VOLATILE* __my_free_list          = _S_free_list + _S_freelist_index(__n);      // Acquire the lock here with a constructor call.      // This ensures that it is released in exit or during stack      // unwinding.#     ifndef _NOTHREADS      /*REFERENCED*/      _Lock __lock_instance;#     endif      _Obj* __RESTRICT __result = *__my_free_list;      if (__result == 0)        __ret = _S_refill(_S_round_up(__n));      else {        *__my_free_list = __result -> _M_free_list_link;        __ret = __result;      }    }    return __ret;  };  /* __p may not be 0 */  static void deallocate(void* __p, size_t __n)  {    if (__n > (size_t) _MAX_BYTES)      malloc_alloc::deallocate(__p, __n);    else {      _Obj* __STL_VOLATILE*  __my_free_list          = _S_free_list + _S_freelist_index(__n);      _Obj* __q = (_Obj*)__p;      // acquire lock#       ifndef _NOTHREADS      /*REFERENCED*/      _Lock __lock_instance;#       endif /* _NOTHREADS */      __q -> _M_free_list_link = *__my_free_list;      *__my_free_list = __q;      // lock is released here    }  }  static void* reallocate(void* __p, size_t __old_sz, size_t __new_sz);} ;</span>

内存布局图:



下面重点介绍下内存分配的过程

1) allocate 上面已有介绍

/**
 * 1) n > 128-------> 一级配置器 malloc
 * 2) 获取n对应的free_list槽位,不是空分配一个并调整剩余空间
 * 3) 若为空,调整n到8的整数倍, 调用refill获取新的空间,
 * 如果refill返回保证会有可用空间,为上层提供可靠服务
 */

2) refill

<span style="font-size:14px;">/* * 1) 以20倍原大小, 先调用chunk_alloc, 如果空间足够会预先获取空间备用. * 2) chunk_alloc成功返回,保证至少有一个可用空间 * 3) 如果可用空间仅一个, 直接返回 * 4) 多余一个, 取出第一个用作返回,其他剩余添加到响应的free_list槽位 */template <bool __threads, int __inst>void*__default_alloc_template<__threads, __inst>::_S_refill(size_t __n){    int __nobjs = 20;    char* __chunk = _S_chunk_alloc(__n, __nobjs);    _Obj* __STL_VOLATILE* __my_free_list;    _Obj* __result;    _Obj* __current_obj;    _Obj* __next_obj;    int __i;    if (1 == __nobjs) return(__chunk);    __my_free_list = _S_free_list + _S_freelist_index(__n);    /* Build free list in chunk */      __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 -> _M_free_list_link = 0;            break;        } else {            __current_obj -> _M_free_list_link = __next_obj;        }      }    return(__result);}</span>

3) chunk_alloc

<span style="font-size:14px;">/** * 1) 计算要获取的最大总空间total_bytes,以及剩余空间bytes_left * 2) 如果total_bytes > bytes_left, 直接分配返回 * 3) 若干至少能满足一个,即bytes_left >= __size, 分配最大可分配空间,并返回 *上述两种是从内存池中获取空间. * 4) 若还不能满足,只能向heap空间求助.分配一个2 * total_bytes + _S_round_up(_S_heap_size >> 4) * 更大的空间,bytes_to_get * 5) 现将内存池中剩余的小空间加到相应的free_list槽位 * 6) 向heap索要bytes_to_get空间, * 成功则增大内存池空间,这时的内存池能够满足要求了,在调用chunk_alloc进行新一轮分配,并返回 * 7) 若失败, 则说明heap空间不够,于是转向free_list中比size大的槽位, * 查找能够一个可用的节点. * 8) 若找到这样的节点,以它作为内存池空间,调用chunk_alloc进行新一轮分配,并返回 * 9) 若free_list不存在这样的节点,则求助与一级分配器 * (char*)malloc_alloc::allocate(__bytes_to_get); * 一级分配器在内存不足时,会回调用相应的处理函数去榨取空间,若失败,默认为抛异常或终止程序. * 这个处理过程可以由客户定义自己的行为. * */template <bool __threads, int __inst>char*__default_alloc_template<__threads, __inst>::_S_chunk_alloc(size_t __size,                                                             int& __nobjs){    char* __result;    size_t __total_bytes = __size * __nobjs;    size_t __bytes_left = _S_end_free - _S_start_free;    if (__bytes_left >= __total_bytes) {        __result = _S_start_free;        _S_start_free += __total_bytes;        return(__result);    } else if (__bytes_left >= __size) {        __nobjs = (int)(__bytes_left/__size);        __total_bytes = __size * __nobjs;        __result = _S_start_free;        _S_start_free += __total_bytes;        return(__result);    } else {        size_t __bytes_to_get =  2 * __total_bytes + _S_round_up(_S_heap_size >> 4);        // Try to make use of the left-over piece.        if (__bytes_left > 0) {            _Obj* __STL_VOLATILE* __my_free_list =                        _S_free_list + _S_freelist_index(__bytes_left);            ((_Obj*)_S_start_free) -> _M_free_list_link = *__my_free_list;            *__my_free_list = (_Obj*)_S_start_free;        }        _S_start_free = (char*)malloc(__bytes_to_get);        if (0 == _S_start_free) {            size_t __i;            _Obj* __STL_VOLATILE* __my_free_list;   _Obj* __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 <= (size_t) _MAX_BYTES;                 __i += (size_t) _ALIGN) {                __my_free_list = _S_free_list + _S_freelist_index(__i);                __p = *__my_free_list;                if (0 != __p) {                    *__my_free_list = __p -> _M_free_list_link;                    _S_start_free = (char*)__p;                    _S_end_free = _S_start_free + __i;                    return(_S_chunk_alloc(__size, __nobjs));                    // Any leftover piece will eventually make it to the                    // right free list.                }            }   _S_end_free = 0;// In case of exception.            _S_start_free = (char*)malloc_alloc::allocate(__bytes_to_get);            // This should either throw an            // exception or remedy the situation.  Thus we assume it            // succeeded.        }        _S_heap_size += __bytes_to_get;        _S_end_free = _S_start_free + __bytes_to_get;        return(_S_chunk_alloc(__size, __nobjs));    }}</span>

3 对外提供的简单接口

<span style="font-size:14px;">template<class _Tp, class _Alloc>class simple_alloc {public:    static _Tp* allocate(size_t __n)      { return 0 == __n ? 0 : (_Tp*) _Alloc::allocate(__n * sizeof (_Tp)); }    static _Tp* allocate(void)      { return (_Tp*) _Alloc::allocate(sizeof (_Tp)); }    static void deallocate(_Tp* __p, size_t __n)      { if (0 != __n) _Alloc::deallocate(__p, __n * sizeof (_Tp)); }    static void deallocate(_Tp* __p)      { _Alloc::deallocate(__p, sizeof (_Tp)); }};</span>












0 0