STL之内存管理

原创 2015年07月07日 21:42:24

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)();
#endif

public:

  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>












相关文章推荐

STL中vector的内存管理与简单代码实现

vector 的内部实现其实就是一块连续内存,它和传统的array不同的是支持扩容,不用考虑越界。vector的迭代器就是最简单的指向容器内类型的指针。其内部有三个指针,start(指向数据存储区的首...

教你编写STL的string类-01(理解C/C++内存管理)

STL里的string类相信许多人都用过,它是C++提供的对于字符串操作的类,它与C的字符数组相比,最大的优点就是操作方便,使用它进行存储字符串,或者打印字符串时,不用担心开辟的存储空间是否够用,因为...

STL容器存储的内容动态分配情况下的内存管理

看下面两段代码, typedef pairVirObjTYPE, std::listCheckID>*> VirObj_CheckID_pair;class LangChecker{public: ...

SGI STL 的内存管理

1. 好多废话     在分析完nginx的内存池之后,也想了解一下C++的内存管理,于是就很自然得想到STL。 STL是一个重量级的作品,据说当时的出现,完全可以说得上是一个划时代意义的作品。 ...

STL内存管理

前两天腾讯

不得不说的故事:STL内存管理

1. 概述 STL Allocator是STL的内存管理器,也是最低调的部分之一,你可能使用了3年stl,但却不知其为何物。 STL标准如下介绍Allocator the STL include...
  • yfkiss
  • yfkiss
  • 2011年07月25日 23:17
  • 8246

STL源码学习----内存管理

1, allocator SGI STL 的头文件defalloc.h中有一个符合标准的名为allocator的内存分配器,它只是简单地将::operator new 和::operator d...
  • lqygame
  • lqygame
  • 2017年06月07日 19:29
  • 86

stl内存管理

stl内存管理

教你编写STL的string类-02(理解C/C++内存管理)

上一篇为大家介绍了如何使用深复制实现string类,这篇为大家介绍如何使用引用计数实现string类。 深复制是利用开辟新空间来防止内存被重复释放,而引用计数又是另外一个极端,它利用内存共享来防止内...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:STL之内存管理
举报原因:
原因补充:

(最多只允许输入30个字)