美文网首页
SGI空间配置器

SGI空间配置器

作者: _ace2 | 来源:发表于2020-05-09 10:59 被阅读0次

标准配置器

SGI定义有一个符合标准的配置器std::allocator,但这个配置器并没有对内存分配做任何优化,只是对::operator new 和 ::operator delete 做 一 层 薄 薄 的 包 装 而 已。SGI并没有使用这个配置器,也不建议我们使用。

特殊配置器std::alloc

std::alloc是一个特殊的配置器,并不符合标准规范,如STL规范规定配置器必须拥有名为 construct() 和 destroy() 的两个成员函数,但std::alloc就没有这两个函数。
但SGI真正使用的配置器就是std::alloc,SGI STL每个容器都指定std::alloc为其默认配置器。

template <class T, class Alloc = alloc> // 使用 alloc 为配置器
class vector { ... };

new和delete表达式:

平时我们new一个类的时候如:

Complex* pc = new Complex(1,2);

编译器会转为:

Complex *pc;
try{
      void*  mem = operator new(sizeof(Complex));   //(1)
      pc = static_cast<Complex*>(mem);
      pc->Complex::Complex(1,2);       //(2)
}
catch(std::bad_alloc){}

就是把表达式new分为两个操作,(1)调用operator new函数配置内存;(2)调用类的构造函数。

//编译器内部
pc->~Complex();   //(1)
operator delete(pc); //(2)

调用delete表达式也一样,(1)调用类析构函数;(2)调用operator delete函数释放内存。
std::alloc配置器把这两个阶段的操作区分开来,内存配置动作由alloc:allocate() 负责,内存释放动作由 alloc::deallocate() 负责;对象建构动作由 ::construct() 负责,对象解构动作由 ::destroy() 负责。且这几个函数分别位于两个文件中:

#include <stl_alloc.h>  // 负责内存空间的配置与释放
#include <stl_construct.h>   // 负责对象内容的建构与解构

负责构造和析构的两个函数 construct() 和 destroy()

construct():
#include <new.h> // 欲使用 placement new ,需先含入此檔
template <class T1, class T2>
inline void construct(T1* p, const T2& value) {
     new (p) T1(value);
     /*new (p) T1(value):
    上面已经说过,new表达式在编译器内被分成两个阶段操作,这里也一样,会被分成:
    T1*pc;
    void*  mem = operator new(sizeof(T1),p);   //(1)
    pc = static_cast<T1*>(mem);
    pc->Complex::Complex(1,2);       //(2)
    
    //operator new什么都没做,只是返回传入的指针
    void* operator new(size_t size,void* start){
            return start;
    }
    所以new (p) T1(value);相当于只是调用了T1的构造函数
  */
}
destroy()

destroy有两个版本:

// 以下是 destroy() 第一版本,接受参数。
template <class T>
inline void destroy(T* pointer) {
        pointer->~T();  // 唤起 dtor ~T()
}
// 以下是 destroy() 第二版本,接受两个迭代器。此函式设法找出元素的数值型别,
// 进而利用 __type_traits<> 求取最适当措施。
template <class ForwardIterator>
inline void destroy(ForwardIterator first, ForwardIterator last) {
      __destroy(first, last, value_type(first));
}

第二个版本关联的函数:

// 判断元素的数值型别( value type )是否有 trivial destructor
template <class ForwardIterator, class T>
inline void __destroy(ForwardIterator first, ForwardIterator last, T*)
{
       typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
       __destroy_aux(first, last, trivial_destructor());
}
// 如果元素的数值型别( value type )有 non-trivial destructor…
template <class ForwardIterator>
inline void
__destroy_aux(ForwardIterator first, ForwardIterator last, __false_type) {
      for ( ; first < last; ++first)
          destroy(&*first);
}
// 如果元素的数值型别( value type )有 trivial destructor…
template <class ForwardIterator>
inline void __destroy_aux(ForwardIterator, ForwardIterator, __true_type) {}

// 以下是 destroy() 第二版本针对迭代器为 char* 和 wchar_t* 的特化版
inline void destroy(char*, char*) {}
inline void destroy(wchar_t*, wchar_t*) {}
destroy示意图.png

第二个版本泛化的会去判断对象的析构函数释放是否是trivial destructor,如果是就什么都不做(也就是不调用对象的析构函数,对untrivial destructor的最终做的也只是调用对象析构函数),如果不是就一个个调用对象析构函数。析构函数的作用就是释放其对象中手动分配的内存,如果析构函数中没有释放内存的操作,那么调不调用析构函数都无所谓,当然平时写程序不提倡这么做,养成好习惯。所以在destroy()中,如果对象析构函数是trivial destructor(如基本内置类型),则析构函数内就没有释放内存操作,就什么都不用做,若是untrivial destructor,说明析构函数内有可能有释放内存操作,就必须一个个调用析构函数,否则会内存泄露。

空间配置和释放

内存管理1.png

C++中,分配器(allocator)、new、new[]等内存配置操作最终都是经过malloc和free来完成内存的配置和释放。
SGI采用了双层级配置器,第一级直接使用malloc()和free(),第二级则根据需要配置的大小决定,如果需要配置区块超过128bytes时,调用第一级配置器配置(在下面源码中可以看到),如果不超过128bytes,为了避免太多小区块造成内存碎片和减少额外负担(去除cookie,减少浪费内存),采用复杂的memory pool的管理方式。

static void * allocate(size_t n)
{
        // .........
        // 大于 128 就呼叫第一级配置器
        if (n > (size_t) __MAX_BYTES)
            return(malloc_alloc::allocate(n));
            /*malloc_alloc::allocate就是调用malloc:
            static void * allocate(size_t n)
            {
                  void *result = malloc(n);
                  if (0 == result) result = oom_malloc(n);
                  return result;
             }
            */
       }
      // .........
}
simple_alloc

为了让配置器接口符合STL规格( std::alloc 并不接受任何 template 型别参数),SGI还为sdt::alloc包装一个接口:

template<class T, class Alloc>
class simple_alloc {
public:
    static T *allocate(size_t n)
    {
        return 0 == n ? 0 : (T*)Alloc::allocate(n * sizeof(T));
    }
    static T *allocate(void)
    {
        return (T*)Alloc::allocate(sizeof(T));
    }
    static void deallocate(T *p, size_t n)
    {
        if (0 != n) Alloc::deallocate(p, n * sizeof(T));
    }
    static void deallocate(T *p)
    {
        Alloc::deallocate(p, sizeof(T));
    }
}; 

std::alloc接收的参数是bytes,一次配置和释放多少个字节,包装后的simple_alloc接收的参数是元素的个数,一次配置和释放多少个元素。
SGI STL容器都使用simple_alloc这个接口。如:

template <class T, class Alloc = alloc> // 预设使用 alloc 为配置器
class vector {
protected:
    // 专属之空间配置器,每次配置一个元素大小
    typedef simple_alloc<value_type, Alloc> data_allocator;
    void deallocate() {
        if (...)
            data_allocator::deallocate(start, end_of_storage - start);
    }
    ...
};

相关文章

  • 空间配置器

    2.空间配置器 2.1具备次配置力(sub-allocation)的SGI空间配置器 SGI含有两个空间配置器类,...

  • SGI空间配置器

    标准配置器 SGI定义有一个符合标准的配置器std::allocator,但这个配置器并没有对内存分配做任何优化,...

  • stl源码剖析笔记-空间配置器

    SGI stl中容器默认使用的空间配置器是alloc。 空间:可以存储数据的地方,可以是内存,磁盘,甚至设备等,能...

  • 空间配置器

    空间的配置和释放 1、考虑到小型区块所可能造成的内存破碎问题,SGI 设计了双层级配置器 a: 第一级配置区块超过...

  • C++STL内存分配

    STL文件的包含关系: SGI STL 设计了双层级配置器。第一层配置器直接使用malloc() 和 free()...

  • 配置器(allocator)

    配置器(allocator) 配置器:负责空间配置与管理,从实现的角度来看,配置器是一个实现了动态空间配置、空间管...

  • STL源码剖析

    空间配置器 分为第一级空间配置器,和第二级空间配置器 配合使用 第一级空间配置器分配大内存大于128bytes...

  • STL源码解析-空间配置器

    STL源码解析-空间配置器 STL中非常重要的一个模块就是空间配置器,用来管理程序内存的。 空间配置器 这是stl...

  • (1) SGI的Allocator分析

    C++标准的内存配置和释放动作: 在sgi STL中:内存配置--> alloc::allocator()内存释放...

  • allocator详解

    STL空间配置器allocator详解

网友评论

      本文标题:SGI空间配置器

      本文链接:https://www.haomeiwen.com/subject/vxwqnhtx.html