一起了解C++ SGI版本allocator

前言

这也是面试官喜欢问的东西,咱们这次整理清楚,做好准备。

为什么要有allocator?

《stl源码剖析》提到,stl库的六大组件:容器、算法、迭代器、仿函数、适配器、配置器(allocator)。
这个问题在《primer C++》中写的很清楚。作者认为,就不应该将allocation和construction搞在一起(对没错,说的就是new),当然delete也很挫。为什么呢?因为有的时候我确实需要分配内存,但是一次分配的内存过多;分配多了也就算了(不能算了),分配完了以后还顺便给我构造了,这就更浪费cpu的资源了。
When we allocate a block of memory, we often plan to construct objects in thatmemory as needed. In this case, we’d like to decouple memory allocation from objectconstruction. Decoupling construction from allocation means that we can allocatememory in large chunks and pay the overhead of constructing the objects only whenwe actually need to create them.
再者,执行new的时候会顺便帮我执行构造函数,比如默认构造函数或者是拷贝构造函数。可是接下来我就会对内存上的变量进行一些赋值操作,这不就又做了重复劳动么!

四个主要功能

其实只要是满足allocator标准,咱们自己也可以动手写一个自己的allocator。allocator需要按照标准实现以下四点主要功能( 当然还有其他功能):
1.allocator(分配一段原始的,未构造的内存)
2.deallocator(回收一段原始的内存:如果被construct过的话一定要先destroy一下)
3.construct构造函数
4.destroy析构函数
allocator称为空间配置器,而不是内存配置器,是因为allocator可以向磁盘取空间。

一步步看看源码

我的VS用的应该不是SGI版本的stl库:

// CLASS TEMPLATE vector
template <class _Ty, class _Alloc = allocator<_Ty>>
class vector { // varying size array of values

SGI版本应该会显示为class _Alloc = alloc<>这种。咱们的VS编译器可能是PJ版本的stl实现。
STL标准要求STL实现一定要有std::allocator这个配置器,但是SGI版本实现并没有遵循,而是用的自己的一套std::alloc,而SGI里面的std::allocator,仅仅是对new和delete的简单封装。
所以我们研究SGI版本的alloc。
整个配置器分为两级,第一级配置器malloc_alloc和第二级配置器default_alloc,默认是后者(更高级的第二级)。
为什么要搞这种两层设计呢?是因为咱们申请的一些小的内存可能会造成内存碎片。第一级allocator呢,直接调用malloc和free进行内存的分配与释放,这就是简单的内存堆方式,用struct管理内存,作为头部信息。第二级allocator呢,就复杂一点了,它会判断你这次申请的内存大小有没有超过128Bytes。如果超过了,那么直接调用第一级allocator的方式处理,结束。如果没有超过,那么会用一些更棒的内存分配方式,如memery pool。内存池的好处在于,不仅这是一块事先就malloc好的内存,且可以方便实现内存碎片的整合。而且你还记得么?内存池会搞很多格式的内存大小哦!
刚刚咱们说,alloc不符合stl标准,咋办?那就再对alloc进行一层封装,叫simple_alloc,于是呢,SGI版本的stl就是直接对这个simple_alloc进行的调用。
咱们先来看看第一级allocator长啥样:

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;
  }
};

// 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

你看!人家的alloc就直接用malloc实现了。。。。。。
OK咱们继续来看看第二级allocator的实现:

template <bool threads, int inst>
class __default_alloc_template {
private:

  //获取bytes最适合的块大小,比如7->8, 9->16, 22->24
  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];
  };
private:
  static _Obj* __STL_VOLATILE _S_free_list[]; 
  static  size_t _S_freelist_index(size_t __bytes) {
        return (((__bytes) + (size_t)_ALIGN-1)/(size_t)_ALIGN - 1);
  }
  //返回一个size为__n的对象,然后加入到对应的free list
  static void* _S_refill(size_t __n);
  //配置一大块内存
  static char* _S_chunk_alloc(size_t __size, int& __nobjs);
  //内存池起始位置和结束位置
  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
    class _Lock;
    friend class _Lock;
    class _Lock {
        public:
            _Lock() { __NODE_ALLOCATOR_LOCK; }
            ~_Lock() { __NODE_ALLOCATOR_UNLOCK; }
    };
public:
  static void* allocate(size_t __n);
  static void deallocate(void* __p, size_t __n);
  static void* reallocate(void* __p, size_t __old_sz, size_t __new_sz);
} ;

相信看过内存池实现的朋友应该很清楚了,这里搞了个内存池链表,每个链表上是一种规格的内存,都是实现过的东西了,不说了。

猜你喜欢

转载自blog.csdn.net/weixin_44039270/article/details/106730036
今日推荐