【STL】空间配置器剖析(二)

上篇文章点击打开链接主要对于对象的构造含和析构进行了主要说明,这篇文章将对对象构造前的内存配置和对象析构后的空间释放进行深入探索。

好的,话不多说马上进入是正文:

对对象构造前的内存配置和对象析构后的空间释放,由<stl_alloc.h>负责,SGI对此的设计哲学如下:

向system heap要求空间。

考虑多线程的状态

考虑内存不足的应变措施

考虑过多的“小型区块”可能造成的内存碎片问题

C++的内存配置的基本操作是:operator new(),内存释放的基本操作是operator delete。这两个全局函数相当于C的malloc和free函数,正是如此,SGI正是以malloc() 和free()

完成内存的配置与释放。

考虑到小型区块可能造成的内存破碎的问题,SGI设计了双层配置器:

第一层配置器_mallocx_alloc_template<0>malloc_alloc(typedef malloc_alloc alloc);直接使用malloc和free,

第二层配置器:_default_alloc_template<_NODE_ALLOCATOR_THREADS,0> alloc;采用不同得策略,当要配置的空间大于128bytes时,转交给第一层配置器配置,小于128bytes时,为了降低额外的负担,便采用复杂的内存池技术memory pool。

整个设计究竟只是开房第一层,还是同时开放第二层,取决于——USE_MALLOC,是否被定义。alloc不接受任何template型别的参数。

为了使配置器的接口符合STL规格,SGI为他包装了一个接口,simple_alloc;

SGI STL提供两级空间配置器,第一级空间配置器使用malloc/free函数,当分配的空间大小超过128 bytes的时候使用第一级空间配置器;第二级空间配置器使用了内存池技术,当分配的空间大小小雨128 bytes的时候,将使用第二级空间配置器。

大量分配小块的内存空间会带来问题:一是从运行库的堆管理器中取得的内存(比如通过malloc获得的内存),会有一部分空间用于存储管理信息,用于管理各个内存块,这样内存的使用率就降低了;二是过多的小块内存会带来内存碎片问题;采用合适的内存池技术可以避免这些问题。

SGI STL的第一级内存配置器就是简单的使用malloc和free,realloc,他还有对于malloc,realloc的out_of_memory处理机制(oom_malloc,oom_realloc),就是当内存分配失败的时候的一些处理机制,

oom_malloc不断的进行尝试释放,配置,释放配置......这个释放操作其实是自己写的一个处理函数,set_malloc_handler()(仿造set_new_handler(),以operator new配置的内存才可以,使用set_new_handler处理机制。),一般的处理原则是使得更多的内存空间可用。

SGI STL的第二级内存配置器维护了一个free-list数组,分别用于管理8,16,24,32,40,48,56,64,72,80,88,96,104,112,120,128 bytes的小额区块,free-list的节点结构如下:

union obj
{
    union obj* free_list_link;
    char client_data[1];
};

这里使用union结构,是为了节省空间,也就是说,当节点位于free-list时,通过free_list_link指向下一块内存,而当节点取出来分配给用户使用的时候,整个节点的内存空间对于用户而言都是可用的,这样在用户看来,就完全意识不到free_list_link的存在,可以使用整块的内存了。

在分配内存时,会将大小向上调整为8的倍数,因为free-list中的节点大小全是8的倍数。

 enum {_ALIGN = 8};
    enum {_MAX_BYTES = 128};
    enum {_NFREELISTS = 16}; // _MAX_BYTES/_ALIGN
  // 将待分配的空间大小向上调整为8的倍数
  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:
  // 定义free_list数组
  static _Obj* __STL_VOLATILE _S_free_list[_NFREELISTS];
  // 根据空间大小取得free_list数组的对应下标
  static  size_t _S_freelist_index(size_t __bytes) {
        return (((__bytes) + (size_t)_ALIGN-1)/(size_t)_ALIGN - 1);
  }

空间的分配:就是从对应的free-list节点链表中取出一个节点返回给用户,当然,如果没有可用的节点的话就要通过refill来分配新的节点了,后面会有描述:

static void* allocate(size_t __n)
  {
    void* __ret = 0;
    // 如果大于128 bytes,则使用第一级空间配置器
    if (__n > (size_t) _MAX_BYTES) {
      __ret = malloc_alloc::allocate(__n);
    }
    else {
      // 通过大小取得free-list数组下标,随后取得对应节点的指针
      // 相当于&_S_free_list[_S_freelist_index(__n)]
      _Obj* __STL_VOLATILE* __my_free_list
          = _S_free_list + _S_freelist_index(__n);
      _Obj* __RESTRICT __result = *__my_free_list;
      // 如果没有可用的节点,则通过_S_refill分配新的节点
      if (__result == 0)
        __ret = _S_refill(_S_round_up(__n));
      else {
        // 将当前节点移除,并当做结果返回给用户使用
        *__my_free_list = __result -> _M_free_list_link;
        __ret = __result;
      }
    }

    return __ret;
  };

而空间的回收,则是把内存重新加入到free-list对应的节点链表上去。

那么,当对应的free-list链表中没有可用节点的时候,refill进行了怎样的操作呢?默认操作时通过_S_chunk_alloc从内存池中取得20个新的节点添加到free-list链表中,当然,内存池中的内存不够用也是会出现的情况之一,这时候能分多少就分多少节点,再万一内存池一个节点都提供不了了,那就内存池需要新增空间了,如果失败,再抛出bad_alloc异常。

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);
    // 如果分配了不止一个节点,那么多余的我们要放到free-list里面去
    __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) {
            // 最后一个节点的_M_free_list_link指针指向NULL,并跳出循环
            __current_obj -> _M_free_list_link = 0;
            break;
        } else {
            __current_obj -> _M_free_list_link = __next_obj;
        }
      }
    return(__result);
}

总结一下:

SGI STL提供两级空间配置器,第一级空间配置器使用malloc/free函数,当分配的空间大小超过128 bytes的时候使用第一级空间配置器;第二级空间配置器使用了内存池技术,当分配的空间大小小雨128 bytes的时候,将使用第二级空间配置器。第二级内存配置器维护了一个free-list数组,分别用于管理8,16,24,32,40,48,56,64,72,80,88,96,104,112,120,128
bytes的小额区块,进行小区块的内存分配,具体内容可以参考《STL源码剖析》;

时间: 2024-11-03 22:08:07

【STL】空间配置器剖析(二)的相关文章

STL空间配置器那点事

STL简介 STL(Standard Template Library,标准模板库),从根本上说,STL是一些“容器”的集合,这些“容器”有list,vector,set,map等,STL也是算法和其他一些组件的集合. 谈及组件,那么我们就首先来简单谈下STL六大组件,其相关的设计模式使用,以及各组件之间的协作关系. 设计模式一览 六大组件简单介绍 1. 空间配置器:内存池实现小块内存分配,对应到设计模式--单例模式(工具类,提供服务,一个程序只需要一个空间配置器即可),享元模式(小块内存统一由

stl空间配置器线程安全问题补充

摘要 在上一篇博客<STL空间配置器那点事>简单介绍了空间配置器的基本实现 两级空间配置器处理,一级相关细节问题,同时简单描述了STL各组件之间的关系以及设计到的设计模式等. 在最后,又关于STL空间配置的效率以及空间释放时机做了简单的探讨. 线程安全问题概述 为什么会有线程安全问题? 认真学过操作系统的同学应该都知道一个问题. first--进程是系统资源分配和调度的基本单位,是操作系统结构的基础,是一个程序的运行实体,同时也是一个程序执行中线程的容器 seconed--进程中作为资源分配基

STL空间配置器

1.什么是空间配置器? 空间配置器负责空间配置与管理.配置器是一个实现了动态空间配置.空间管理.空间释放的class template.以内存池方式实现小块内存管理分配.关于内存池概念可以点击:内存池. 2.STL空间配置器产生的缘由 在软件开发,程序设计中,我们不免因为程序需求,使用很多的小块内存(基本类型以及小内存的自定义类型).在程序中动态申请,释放.这个过程过程并不是一定能够控制好的,于是乎出现以下问题: 问题1:就出现了内存碎片问题.(ps外碎片问题) 问题2:一直在因为小块内存而进行

STL——空间配置器(SGI-STL)

一. 空间配置器标准接口 参见<STL源码剖析>第二章-2.1. 二.具备次配置力的SGI空间配置器 SGI STL的配置器与众不同,也与标准规范不同,其名称是alloc而非allocator,而且不接受任何参数(虽然SGI也定义有一个符合部分标准.名为sllocator的配置器,但SGI自己从未用过它,也不建议使用,主要因为效率不佳).这并不会带来什么困扰:我们通常很少需要自行指定配置器名称,而SGI STL的每一个容器都已经指定其缺省的空间配置器为alloc. // 在程序中要明白采用SG

stl 空间配置器理解

理解了一下stl的空间配置器,发现一个比较好的学习方法,跟着代码自己也跟着写一遍,顺便加些注释,可以更加帮助自己理解. 如new,delete一般,分为两个步骤,1,配置空间,2,构造对象(1,析构对象,2,释放空间) 一.构造和析构的基本工具(construct,destroy) 1,construct(构造) template<class T1,class T2> inline void construct(T1 * p,const T2 & value){ new (p) T1(

STL空间配置器(一)

STL空间适配器(一) Author:胡建 Time:2016/4/5 这是STL学习的第一部分,空间适配器,所谓空间适配器,就是用来管理内存的一个器具.对于STL来说,空间适配器是它可以正常工作的基础,也为它可以高效工作提供了动力.对于使用STL来说,它是不和用户直接打交道的,而是隐藏在一切STL组建之后,默默为各种内存申请提供支持的. 对于c++用户来说,new和delete很熟悉,这两个函数可以分别完成内存的申请和释放,和c里面的malloc和free如出一辙,SGI 有一个标准空间适配器

STL空间配置器、vector、list、deque、map复习

本文写于2017-03-03,从老账号迁移到本账号,原文地址:https://www.cnblogs.com/huangweiyang/p/6440830.html STL的六大组件:容器.算法.迭代器.空间配置器.容器适配器.仿函数. 空间配置器 空间配置器产生的缘由:由于程序需求,很多小块内存在程序中动态申请.释放.于是就容易出现内存外部碎片问题,同时由于一直调用malloc系统调用,产生性能问题. (注:内碎片:因为内存对齐/访问效率而差生如用户需要3字节,实际得到4字节的问题,其中的碎片

stl空间配置器alloc

SGI设计了双层级配置器,第一级配置器直接使用malloc()和free(),第二级配置器视情况采用不同的策略:当配置区块超过128bytes时,调用第一级配置器,当配置区块小于128bytes时,采用内存池方式 //SGI第一级配置器 template<int inst> class __malloc_alloc_template { private: //处理内存不足的情况 //c++ new handler机制:系统在内存配置需求无法被满足时,调用一个指定函数,参见effective c

STL——空间配置器

__malloc_alloc_template分配器:该分配器是对malloc.realloc以及free的封装: 当调用malloc和realloc申请不到内存空间的时候,会改调用oom_malloc()和oom_realloc(),这两个函数会反复调用用户传递过来的out of memory handler处理函数,直到能用malloc或者realloc申请到内存为止. 如果用户没有传递__malloc_alloc_oom_handler,__malloc_alloc_template会抛出