STL源码剖析 空间配置器 查漏补缺

ptrdiff_t含义 

  • 减去两个指针的结果的带符号整数类型
  • ptrdiff_t (Type support) - C 中文开发手册 - 开发者手册 - 云+社区 - 腾讯云

std::set_new_handler()函数的理解

  • 关于set_new_handler的理解_wck0617-CSDN博客
  • new分配内存的时候  如果分配的空间不足 采取什么样的措施

自己实现空间配置器

#include <new>        //for placement new
#include <cstddef>    //for ptrdiff_t size_t
#include <cstdlib>    //for exit
#include <climits>    //for UINT_MAX
#include <iostream>   //for cerr
#include <vector>namespace Chy{template <class T>inline T* _allocate(ptrdiff_t size,T*){std::set_new_handler(0);T* tmp = (T*)(::operator new((std::size_t)(size * sizeof (T))));if (tmp == 0){std::cerr << "out of memory" << std::endl;exit(1);}return tmp;}template<class T>inline void _deallocate(T* buffer){::operator delete (buffer);}template<class T1,class T2>inline void _construct(T1 *p,const T2& value){new(p) T1 (value);  //没看懂}template <class T>inline void _destroy(T* ptr){ptr->~T();}template <class T>class allocator{public:typedef T           value_type;typedef T*          pointer;typedef const T*    const_pointer;typedef T&          reference;typedef const T&    const_reference;typedef std::size_t size_type;typedef ptrdiff_t   difference_type;template<class U>struct rebind{typedef allocator<U>other;};pointer allocate(size_type n,const void * hint = 0){return _allocate((difference_type)n,(pointer)0);}void deallocate(pointer p,size_type n){_deallocate(p);}void construct(pointer p,const T& value){_construct(p,value);}void destroy(pointer p){_destroy(p);}pointer address(reference x){return (pointer)&x;}const_pointer const_address(const_reference x){return (const_pointer)&x;}size_type max_size()const{return size_type(UINT_MAX/sizeof (T));}};
}
int main(){int ia[5] = {0,1,2,3,4};unsigned int i;std::vector<int,Chy::allocator<int>>iv(ia,ia+5);for (int j = 0; j < iv.size(); ++j) {std::cout << iv[j] << " ";}std::cout << std::endl;
}
  • SGI STL 使用了专属的 拥有层级配置的 特殊的空间配置器
  • SGI STL 提供了 一个标准的空间配置器的接口 叫做 simple_alloc

SGI STL 封装的 特殊的空间配置器 alloc

  • 使用的时候 std::vector<int,std::alloc>iv;std::alloc 采用默认的形式
  • 为了精密分工,STL a llo c a to r决定将这两阶段操作区分开来。内存配置操作 由 alloc: al locate ()负责,内存释放操作由alloc : : deallocate ()负责;对象构造操作由::construct:()负责,对象析构操作由:;destroy负责

  • new算式内含两阶段操作: (1 )调 用 ::operator new配置内存; ⑵ 调 用Foo::Foo()构造对象内容。
  • delete算式也内含两阶段操作:(1)调用Foo:~Foo ()将对象析构;(2 ) 调 用 ::operator delete释放内存。

 type_traits

  • c++11——type_traits 类型萃取 - 农民伯伯-Coding - 博客园
  • C++ STL __type_traits解析 - 知乎

构造和析构的基本工具 construct()和destroy()

#include <new>        //for placement new
#include <cstddef>    //for ptrdiff_t size_t
#include <cstdlib>    //for exit
#include <climits>    //for UINT_MAX
#include <iostream>   //for cerr
#include <vector>using namespace std;
template<class T>
struct __type_traits {typedef __true_type this_dummy_member_must_be_first;typedef __false_type has_trivial_default_constructor;typedef __false_type has_trivial_copy_constructor;typedef __false_type has_trivial_assignment_constructor;typedef __false_type has_trivial_destructor;typedef __false_type is_POD_type;
};template<class T1,class T2>
inline void _construct(T1 *p,const T2& value){new(p) T1 (value);  //placement new; 调用T1::T1(value)
}/** destroy() 的两个版本*/
//以下是 destroy() 第一版本 接收一个指针
template <class T>
inline void _destroy(T* ptr){ptr->~T(); //调用 ~T()
}/********************************************************************/
//如果元素的型别(value_type)有non_trivial destructor
template <class ForwardIterator>
inline void __destroy_aux(ForwardIterator first,ForwardIterator last,std::__false_type){for( ; first < last;++first){destroy(&*first); //调用destroy的第一个版本}
}
//如果元素的型别(value_type)有trivial destructor
template <class ForwardIterator>
inline void __destroy_aux(ForwardIterator,ForwardIterator,std::true_type){}//判断元素的型别(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());
}//destroy()第二版本 接收两个迭代器 这个函数设法找出元素的型别
//进而使用 __Type_traits<> 求取适当的措施
template <class ForwardIterator>
inline void destroy(ForwardIterator first,ForwardIterator last){
//    __destroy()
}
/********************************************************************//** 以下是destroy() 第二版本针对迭代器为 char* 和 wchar_t 的特化版本*/
inline void destroy(char*,char*){}
inline void destroy(wchar_t *,wchar_t *){}

  •  第二版本的接受first和last两个迭代器 准备将[first,last)范围内的元素都析构掉,但是如果这段范围很大,对于每一个元素的析构 都 无关痛痒(即 trivial destructor类型的),对于效率是一种损失。
  • 因此 需要进行偏特化的操作,在执行析构函数之前进行类型的判断。如果是 true_type 什么都不做直接结束,如果是false_type的类型,使用循环的方式,针对每个元素使用 第一个版本的destroy()函数进行析构释放
  • 问题:如何实现上述的 value_type() 和 __type_traits<> ??

空间的配置和释放

  • 对象构造之前的空间配置和对象析构之后的空间的释放 由 <stl_alloc.h>负责

设计思路

  • 向系统的heap 申请内存空间
  • 考虑 多线程状态
  • 考虑内存不足的应对措施
  • 考虑小型区块可能造成的内存碎片问题
  • 代码举例 排除了 多线程的处理

设计思想

  • 考虑到小型区块造成的内存破碎问题,SGI设计了双层级配置器,第一层级使用malloc()和free(),第二层级视情况 采用不同的策略。
  • 当配置的区块超过128bytes时 视为足够大 使用第一层级配置器
  • 当配置的区块小于128bytes时 为了降低额外的负担,使用memory pool的方式
  • alloc并不接受任何 template型别的参数
  • 无论alloc使用的是第一级或者第二级配置器 都需要外包一层接口
#ifdef __USE_MALLOC
typedef __malloc_alloc_template<0>malloc_alloc;
typedef malloc_alloc alloc;  //令alloc为第一级配置器//令alloc为第二级配置器
typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS,0>alloc;#endiftemplate<class T,class Alloc>
class simple_alloc{
public:static T* allocate(std::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 (n!=0){Alloc::deallocate(p,n * sizeof(T));}}static void deallocate(T* p){Alloc::deallocate(p,sizeof(T));}
};
  • 内部的四个函数本质上是 单纯的转接调用的关系 调用传递给配置器的(可能是第一级别 也可能是 第二级别)
  • SGI STL容器都使用 这个simple_alloc的接口
template <class T,class Alloc = alloc>
class vector{
protected://专属空间配置器 每次分配一个元素的大小typedef simple_alloc<value_type,Alloc> data_allocator;void deallocate(){if(){data_allocator::deallocate(start,end_of_storage - start);}}
};

第一级配置器 __malloc_alloc_template剖析

#if 0
#   include<new>
#   define __THROW_BAD_ALLOC throw bad_alloc
#elif !defined(__THROW_BAD_ALLOC)
#   include<iostream>
#   define __THROW_BAD_ALLOC std::cerr << "out of memory" << std::endl; exit(1);
#endif//malloc-based allocator 通常比稍后介绍的default alloc要慢
//但是它是线程安全的 thread-safe,并对于空间的运用比较高效
//以下是第一级别配置器
//无“template 型别参数” 至于非型别参数 inst 完全没有用到
template <int inst>
class __malloc_alloc_template{
private://以下内容都是函数指针,所代表的函数用于处理内存不足的情况//oom : out of memorystatic void *oom_malloc(std::size_t);static void *oom_realloc(void*,std::size_t);static void (* __malloc_alloc_oom_handler)();
public:static void* allocate(std::size_t n){void * result = malloc(n);//第一级配置器 直接使用malloc进行内存的分配//当无法满足内存分配的需求的时候,使用oom_malloc()if(result == 0){result = oom_malloc(n);}return result;}static void deallocate(void* p,size_t /* n */){free(p); //第一级配置器 直接使用 free()进行释放}static void* reallocate(void* p,size_t /*old_size*/,size_t new_size){void *result = realloc(p,new_size); //第一级别配置器直接使用realloc()//以下无法满足需求的时候 改用oom_realloc()if (0==result){result = oom_realloc(p,new_size);}return result;}//以下是仿真C++的set_new_handler(),换句话说 你可以通过他指定自己的out_of_handlerstatic void(*set_malloc_handler(void (*f)()))(){void (* old)() = __malloc_alloc_oom_handler;__malloc_alloc_oom_handler = f;return (old);}};
//malloc_alloc out_of_memory handling
//初始化为0
template <int inst>
void (* __malloc_alloc_template<inst>::__malloc_alloc_oom_handler)() = 0;template <int inst>
void* __malloc_alloc_template<inst>::oom_malloc(std::size_t n) {void (*my_malloc_handler)();void * result;for(;;){ //不断尝试 释放、配置、再次释放、再次配置......my_malloc_handler = __malloc_alloc_oom_handler;if (0 == my_malloc_handler){__THROW_BAD_ALLOC;}(*my_malloc_handler)();//调用处理例程,企图释放内存result = malloc(n);    //再次配置内存if (result){return result;}}
}template <int inst>
void *__malloc_alloc_template<inst>::oom_realloc(void *p, std::size_t n) {void (* my_malloc_handler)() = __malloc_alloc_oom_handler;void * result;if(0 == my_malloc_handler){__THROW_BAD_ALLOC;}(*my_malloc_handler)();//调用处理例程,企图释放内存result = realloc(p,n);//再次尝试配置内存if (result){return result;}
}//以下直接将参数 inst 指定为0
typedef __malloc_alloc_template<0>malloc_alloc;
  •  第一级配置器 使用malloc、free、realloc等C函数执行内存的配置、释放、重新配置,并实现了类似C++ newhandler的机制 ,但是不能直接使用C++的new handler的机制 因为他并没有使用 ::operator new的方式来配置内存
  • C++ 的new handler的机制是 要求系统在内存配置需求无法被满足的时候 调用一个用户自定义的函数 即 ::operator new无法完成任务 抛出std::bad_alloc异常状态之前 先调用客户指定的处理例程 。这个处理的例程 被称作new handler
  • 第一级配置器  allocate()和 reallocate()都会在调用malloc 和 realloc不成功之后 改用 oom_malloc 和 oom_realloc 后者函数内设定一个循环,不断调用 内存不足处理的程序  ,期望某一次可以得到足够的内存。如果未指定例程 便会调用 __THROW_BAD_ALLOC 抛出bad_alloc异常信息,或者使用 exit(1) 终止程序

第二级配置器__default_alloc_template 剖析

  • 第二级配置器 使用机制 避免造成内存的碎片 
  • 额外负担 无法避免 涉及到操作系统的内存管理;但是区块越小,显现的 额外的负担的比例就会越来越大,就会显得浪费

  •  大于128转交第一级配置器,小于128使用内存池管理。
  • 次层配置:每次配置大内存,维护对应的自由链表,下次若有相同大小的内存需求,直接从自由链表中拨出
  • 配置器 负责内存的释放和回收 
  • 第二级配置器的小额区块的内存空间是以8的倍数进行划分,划分的时候 会自动将需求量上调至8的倍数
  • 维护16个free_lists 各自管理 8 16 24 32 40 48 56 64 72 80 88 96 104 112 120 128
union obj{union obj * free_list_link;char client_data[1];// The client sees this
};
  • 维护链表是需要 额外的指针 造成额外的负担
  • 考虑到 obj 使用的是union,可以视为一个指针 指向相同类型的另外一个obj ; obj也可以视为一个指针 指向实际的区块  一物二用

enum {__ALIGN = 8}; //小型区块的上调边界
enum {__MAX_BYTES = 128};//小型区块的上限
enum {__NFREELISTS = __MAX_BYTES/__ALIGN};//free-lists的个数using namespace std;
//以下是第二级配置器
//注意:无 template型别参数 且第二参数哇怒气按没有被派上用场
//第一参数用于多线程的环境 本示例 暂未涉及
template <bool threads,int inst>
class __default_alloc_template{
private://ROUND_UP() 将bytes上调至8的倍数static size_t ROUND_UP(size_t bytes){return (((bytes) + __ALIGN - 1) & ~(__ALIGN-1));}private:union obj{    //free-lists 节点构造union obj * free_list_link;char client_data[1];// The client sees this};
private://16个free-listsstatic obj * volatile free_list[__NFREELISTS];//以下函数根据区块的大小 决定使用第 n 号free-list,n从1开始算起static size_t FREELIST_INDEX(size_t bytes){return (((bytes) + __ALIGN-1)/__ALIGN-1);}//返回一个大小为n的对象 并可能加入大小为n 的其他区块到free liststatic void* refill(size_t n);//配置一大块区间 可以容纳nobjs个“size”大小的区块//如果配置nobjs个大小的区块有所不方便 ,可以降低nobjsstatic char * chunk_alloc(size_t size,int &objs);//Chunk allocation statestatic char *start_free;//内存池起始的位置 只在chunk_alloc()中变化static char *end_free;//内存池结束的位置  只在chunk_alloc()中变化static size_t heap_size;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){}
};//以下是static data member 的定义与初值的设定
template <bool threads,int inst>
char* __default_alloc_template<threads,inst>::start_free = 0;template <bool threads,int inst>
char* __default_alloc_template<threads,inst>::end_free = 0;template <bool threads,int inst>
size_t __default_alloc_template<threads,inst>::heap_size = 0;template <bool threads,int inst>
typename __default_alloc_template<threads,inst>::obj * volatile
__default_alloc_template<threads,inst>::free_list[__NFREELISTS] ={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

空间配置函数 allocate()

  • 这个函数首先判断区块的大小 ,大于128 使用第一级配置器,小于128 检查对应的 free list。如果free list之内有可用的区块 就直接拿来使用
  • 如果没有可用的区块,就将区块大小上调至8的倍数边界 使用refill()准备为free list填充空间 
template <bool threads,int inst>
void* __default_alloc_template<threads,inst>::allocate(size_t n) {obj* volatile *my_free_list;obj* result;//大于128就调用第一级配置器if(n>(size_t)__MAX_BYTES){return (malloc_alloc::allocate(n));}//寻找16个free lists中适当的一个my_free_list = free_list + FREELIST_INDEX(n);result = *my_free_list;if (result==0){//没有找到free list,需要重新补充 free listvoid *r = refill(ROUND_UP(n));return r;}//调整free list*my_free_list = result->free_list_link;return result;
}

注意事项

  • 注意事项:类里面声明的静态函数 不能带有{} ,否则后期实现函数逻辑的时候 编译不通过
  • 只有用户自定义的类型作为函数的返回值的时候 需要使用typename关键字

template <bool threads,int inst>
void __default_alloc_template<threads,inst>::deallocate(void *p, size_t n) {obj *q = (obj*)p;obj *volatile * my_free_list;//大于128 就调用第一级配置器if(n > (size_t)__MAX_BYTES){malloc_alloc ::deallocate(p,n);return;}//寻找对应的free listmy_free_list = free_list + FREELIST_INDEX(n);//调整free list 回收区块q->free_list_link = *my_free_list;*my_free_list = q;}

 重新填充 free lists

  • 当 free list中没有可用的区块的时候 需要调用 refill() 函数 为free list重新填充空间,新的空间将取自内存池(通过chunk_alloc完成 )
  • 缺省获得 20个新节点,万一内存池空间不足 获得的节点数可能 小于20
//返回一个大小为n的对象,并且有的时候会适当的位free list增加节点
//假设n已经上调为8的倍数
template <bool threads,int inst>
void* __default_alloc_template<threads,inst>::refill(size_t n) {int nobjs = 20;//调用chunk_alloc() 尝试获取nobjs个区块作为free list的新的节点//注意参数nobjs是pass by reference的方式char* chunk = chunk_alloc(n,nobjs);obj* volatile * my_free_list;obj* result;obj* current_obj,*next_obj;int i;//如果只获得一个区块 这个区块就会被分配给调用者使用 free list无新的节点if (1 == nobjs){return chunk;}//否则准备调整free list 纳入新的节点my_free_list = free_list + FREELIST_INDEX(n);//在chunk空间内 建立free listresult = (obj*) chunk;   //这一块准备返回给客户端//以下导引free list指向新的配置的空间(取自内存池)*my_free_list = next_obj = (obj*)(chunk +n);//以下将free list的各个节点串接起来for(i=1;;i++){ //从1开始 因为第0个将会返回给客户端current_obj = next_obj;next_obj = (obj*)((char *)next_obj + n);if (nobjs-1==i){current_obj->free_list_link = 0;break;} else{current_obj->free_list_link = next_obj;}}return (result);
}

内存池

  • 从内存池取空间 给free list使用  是chunk alloc函数的作用
//假设n已经上调为8的倍数
//注意参数nobjs是pass by reference
/** size:每一个objs的大小+* nobjs:创建objs的数量*/
template <bool threads,int inst>
char* __default_alloc_template<threads,inst>::chunk_alloc(size_t size, int &nobjs) {char* result;size_t total_bytes = size * nobjs;size_t byte_left = end_free - start_free;//内存池剩余空间if (byte_left >= total_bytes){//内存池的空间满足需求量result = start_free;start_free += total_bytes;return result;} else if(byte_left >= size){//内存池存储的空间不能完全满足需求,但是足够供应一个 (含)以上的区块nobjs = byte_left/size; //可以满足的数量total_bytes = nobjs * size;result = start_free;start_free += total_bytes;return (result);} else{//内存池剩余的空间连一个区块的大小都无法满足size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);//以下内容试着从内存池 残余中 寻找if (byte_left > 0){//内存中还存在部分的内存空间 但是这个内存空间小于一个size 但是大于0//首先找到适当的free listobj* volatile* my_free_list = free_list + FREELIST_INDEX(byte_left);//调整free list 将内存池中的残余空间全部编入到 free list里面((obj *)start_free)->free_list_link = *my_free_list;*my_free_list = (obj*) start_free;}//配置heap空间 用于填充内存池start_free = (char*)malloc(bytes_to_get);if (0 == start_free){//heap空间不足 malloc失败int i;obj* volatile * my_free_list,*p;//尝试检视手上具备的东西 这不会造成伤害 我们不打算进一步进行配置较小的区块//因为在多进程的机器上容易导致灾难//以下代码的目的是搜寻适当( 尚有未用的区块,且区块够大 )的free listfor (i = size;i <= __MAX_BYTES;i+- __ALIGN ) {my_free_list = free_list + FREELIST_INDEX(i);p = *my_free_list;if (p!=0){//free list 内尚有未用的区块//调整free list 释放未使用的区块*my_free_list = p->free_list_link;start_free = (char*)p;end_free = start_free + i;//递归调用自己 为了修正nobjsreturn (chunk_alloc(size,nobjs));//注意:任何残余的领头 终将被编入适当的free list中作为备用}}end_free = 0;//如果出现意外(山穷水尽 到处无内存可以使用)//调用第一级配置器 寄希望于第一级配置器 希望out-of-memory机制可以出一份力start_free = (char*) malloc_alloc::allocate(bytes_to_get);//这会抛出异常(exception) 或内存不足的情况获得改善}heap_size += bytes_to_get;end_free = start_free + bytes_to_get;//递归调用自己 为了修正 nobjsreturn (chunk_alloc(size,nobjs));}
}
  • 使用 end_free - start_free 判断内存池的容量,如果空间充足直接调出20个区块返回给free_list
  • 如果不足20个 但是大于1个以上的容量 先提供可以满足的区块 ,递归更新 (pass by reference的nobjs修改为实际可以提供的区块数)
  • 如果一个都不可以满足  使用malloc从heap中配置内存 为内存池注入活水,新水量的大小是需求量的2倍,还需要加上随着配置次数增加愈来愈大的附加量

  • 例子:假设程序一开始客户端 就调用 chunk_alloc(32,20),此刻内存池和free_list里面均没有可用的内存空间,所以使用malloc() 配置40个32byte区块,使用malloc分配的内存空间是需求的两倍。其中第一个 32byte交出,19个给free_list [3] 进行维护,其余的20个留给内存池。
  • 客户端 使用 chunk_alloc(64,20),此刻free_list [7]没有内存 ,向内存池提出申请 ;内存池此刻具备的内存 (32 * 20),所以只可以提供 (32 * 20)/64 = 10个64位区块,返回10个区块,第一个交给客户端,剩余的9个由 free_list [7]。此刻内存池全空
  • 客户端调用chunk_alloc(96,20)此刻free_list [11]和内存池全为空,需要使用malloc分配内存,分配40+n个96bytes区块,将第一个交出,另外19个交给free_list [11]维护,其余的20+n 的容量交给内存池
  • 假设system heap空间不足了 (无法为内存池注入 活水),malloc()行动失败,chunk_malloc()就需要四处寻找合适的内存。找到了就挖一块,这个是从free_list上面去挖内存,找不到就需要第一级配置器。
  • 第一级配置器本质上还是使用malloc() 进行内存的配置,考虑到前提 system heap已经没有内存了,为啥同样使用malloc这里就可以了呢?因为 第一配置器具备out-free-memory处理机制(类似new-handler机制) 就有机会去释放其余地方多余的内存,进行内存的分配和使用。如果可以就成功,如果不可以就返回bad_malloc的异常。

注意事项:

template<class T,class Alloc>
class simple_alloc{};
//SGI 通常使用这种方式来使用配置器
template<class T,class Alloc = alloc> //缺省使用alloc为配置器
class vector{
public:typedef T value_type;
protected://专属空间配置器  每次配置一个元素的大小typedef simple_alloc<value_type,Alloc>data_allocator;
};
  • 其中 第二个template参数所接受的缺省参数值alloc 可以是第一级配置器 也可以是第二级配置器
  • 不过SGI STL已经将其设定为第二级配置器 

完整代码

#if 0
#   include<new>
#   define __THROW_BAD_ALLOC throw bad_alloc
#elif !defined(__THROW_BAD_ALLOC)
#   include<iostream>
#   define __THROW_BAD_ALLOC std::cerr << "out of memory" << std::endl; exit(1);
#endif//malloc-based allocator 通常比稍后介绍的default alloc要慢
//但是它是线程安全的 thread-safe,并对于空间的运用比较高效
//以下是第一级别配置器
//无“template 型别参数” 至于非型别参数 inst 完全没有用到
template <int inst>
class __malloc_alloc_template{
private://以下内容都是函数指针,所代表的函数用于处理内存不足的情况//oom : out of memorystatic void *oom_malloc(std::size_t);static void *oom_realloc(void*,std::size_t);static void (* __malloc_alloc_oom_handler)();
public:static void* allocate(std::size_t n){void * result = malloc(n);//第一级配置器 直接使用malloc进行内存的分配//当无法满足内存分配的需求的时候,使用oom_malloc()if(result == 0){result = oom_malloc(n);}return result;}static void deallocate(void* p,size_t /* n */){free(p); //第一级配置器 直接使用 free()进行释放}static void* reallocate(void* p,size_t /*old_size*/,size_t new_size){void *result = realloc(p,new_size); //第一级别配置器直接使用realloc()//以下无法满足需求的时候 改用oom_realloc()if (0==result){result = oom_realloc(p,new_size);}return result;}//以下是仿真C++的set_new_handler(),换句话说 你可以通过他指定自己的out_of_handlerstatic void(*set_malloc_handler(void (*f)()))(){void (* old)() = __malloc_alloc_oom_handler;__malloc_alloc_oom_handler = f;return (old);}};
//malloc_alloc out_of_memory handling
//初始化为0
template <int inst>
void (* __malloc_alloc_template<inst>::__malloc_alloc_oom_handler)() = 0;template <int inst>
void* __malloc_alloc_template<inst>::oom_malloc(std::size_t n) {void (*my_malloc_handler)();void * result;for(;;){ //不断尝试 释放、配置、再次释放、再次配置......my_malloc_handler = __malloc_alloc_oom_handler;if (0 == my_malloc_handler){__THROW_BAD_ALLOC;}(*my_malloc_handler)();//调用处理例程,企图释放内存result = malloc(n);    //再次配置内存if (result){return result;}}
}template <int inst>
void *__malloc_alloc_template<inst>::oom_realloc(void *p, std::size_t n) {void (* my_malloc_handler)() = __malloc_alloc_oom_handler;void * result;if(0 == my_malloc_handler){__THROW_BAD_ALLOC;}(*my_malloc_handler)();//调用处理例程,企图释放内存result = realloc(p,n);//再次尝试配置内存if (result){return result;}
}//以下直接将参数 inst 指定为0
typedef __malloc_alloc_template<0>malloc_alloc;enum {__ALIGN = 8}; //小型区块的上调边界
enum {__MAX_BYTES = 128};//小型区块的上限
enum {__NFREELISTS = __MAX_BYTES/__ALIGN};//free-lists的个数using namespace std;
//以下是第二级配置器
//注意:无 template型别参数 且第二参数哇怒气按没有被派上用场
//第一参数用于多线程的环境 本示例 暂未涉及
template <bool threads,int inst>
class __default_alloc_template{
private://ROUND_UP() 将bytes上调至8的倍数static size_t ROUND_UP(size_t bytes){return (((bytes) + __ALIGN - 1) & ~(__ALIGN-1));}private:union obj{    //free-lists 节点构造union obj * free_list_link;char client_data[1];// The client sees this};
private://16个free-listsstatic obj * volatile free_list[__NFREELISTS];//以下函数根据区块的大小 决定使用第 n 号free-list,n从1开始算起static size_t FREELIST_INDEX(size_t bytes){return (((bytes) + __ALIGN-1)/__ALIGN-1);}//返回一个大小为n的对象 并可能加入大小为n 的其他区块到free liststatic void* refill(size_t n);//配置一大块区间 可以容纳nobjs个“size”大小的区块//如果配置nobjs个大小的区块有所不方便 ,可以降低nobjsstatic char * chunk_alloc(size_t size,int &nobjs);//Chunk allocation statestatic char *start_free;//内存池起始的位置 只在chunk_alloc()中变化static char *end_free;//内存池结束的位置  只在chunk_alloc()中变化static size_t heap_size;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);
};//以下是static data member 的定义与初值的设定
template <bool threads,int inst>
char* __default_alloc_template<threads,inst>::start_free = 0;template <bool threads,int inst>
char* __default_alloc_template<threads,inst>::end_free = 0;template <bool threads,int inst>
size_t __default_alloc_template<threads,inst>::heap_size = 0;template <bool threads,int inst>
typename __default_alloc_template<threads,inst>::obj * volatile
__default_alloc_template<threads,inst>::free_list[__NFREELISTS] ={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};template <bool threads,int inst>
void* __default_alloc_template<threads,inst>::allocate(size_t n) {obj* volatile *my_free_list;obj* result;//大于128就调用第一级配置器if(n>(size_t)__MAX_BYTES){return (malloc_alloc::allocate(n));}//寻找16个free lists中适当的一个my_free_list = free_list + FREELIST_INDEX(n);result = *my_free_list;if (result==0){//没有找到free list,需要重新补充 free listvoid *r = refill(ROUND_UP(n));return r;}//调整free list*my_free_list = result->free_list_link;return result;
}template <bool threads,int inst>
void __default_alloc_template<threads,inst>::deallocate(void *p, size_t n) {obj *q = (obj*)p;obj *volatile * my_free_list;//大于128 就调用第一级配置器if(n > (size_t)__MAX_BYTES){malloc_alloc ::deallocate(p,n);return;}//寻找对应的free listmy_free_list = free_list + FREELIST_INDEX(n);//调整free list 回收区块q->free_list_link = *my_free_list;*my_free_list = q;
}//返回一个大小为n的对象,并且有的时候会适当的位free list增加节点
//假设n已经上调为8的倍数
template <bool threads,int inst>
void* __default_alloc_template<threads,inst>::refill(size_t n) {int nobjs = 20;//调用chunk_alloc() 尝试获取nobjs个区块作为free list的新的节点//注意参数nobjs是pass by reference的方式char* chunk = chunk_alloc(n,nobjs);obj* volatile * my_free_list;obj* result;obj* current_obj,*next_obj;int i;//如果只获得一个区块 这个区块就会被分配给调用者使用 free list无新的节点if (1 == nobjs){return chunk;}//否则准备调整free list 纳入新的节点my_free_list = free_list + FREELIST_INDEX(n);//在chunk空间内 建立free listresult = (obj*) chunk;   //这一块准备返回给客户端//以下导引free list指向新的配置的空间(取自内存池)*my_free_list = next_obj = (obj*)(chunk +n);//以下将free list的各个节点串接起来for(i=1;;i++){ //从1开始 因为第0个将会返回给客户端current_obj = next_obj;next_obj = (obj*)((char *)next_obj + n);if (nobjs-1==i){current_obj->free_list_link = 0;break;} else{current_obj->free_list_link = next_obj;}}return (result);
}//假设n已经上调为8的倍数
//注意参数nobjs是pass by reference
/** size:每一个objs的大小+* nobjs:创建objs的数量*/
template <bool threads,int inst>
char* __default_alloc_template<threads,inst>::chunk_alloc(size_t size, int &nobjs) {char* result;size_t total_bytes = size * nobjs;size_t byte_left = end_free - start_free;//内存池剩余空间if (byte_left >= total_bytes){//内存池的空间满足需求量result = start_free;start_free += total_bytes;return result;} else if(byte_left >= size){}}

参考链接

  • STL 源码剖析 空间配置器_CHYabc123456hh的博客-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/446296.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python学习16 正则表达式2 re模块

re 模块 re 模块&#xff1a; Python的 re 模块实现了正则表达式处理的功能。 导入re模块后&#xff0c;使用findall、search函数可以进行匹配 查找&#xff1a;match和search 多个匹配上的&#xff0c;也只会返回第一个匹配上的 re.match()&#xff1a; 需要特别注意的是&…

STL源码剖析 内存基本处理工具 初始化空间的五个函数

初始化空间的五个函数构造函数 construct()析构函数 destroy()剩余三个底层函数 和 高层函数之间的对应关系如下uninitialized_copy() 对应 copy()uninitialized_fill() 对应 fill()uninitialized_fill_n() 对应 fill_n()使用<memory>使用上述三个底层函数 uninitiali…

单基因gsea_筛到5分的核心基因以后你可以怎么做?

这一次我们从一些已经发表的文章拆解&#xff0c;我们来看看&#xff0c;你找到了一个核心基因以后&#xff0c;你可以怎么做呢&#xff1f;我们就不说那么多废话了&#xff0c;直接用几篇文章的解读来带着大家领会一下如何去进行下一步的分析。Case1&#xff1a;预后标志物免疫…

STL源码剖析 迭代器的概念和traits编程技法

迭代器&#xff1a;依序巡防某个聚合物(容器)所含的各个元素&#xff0c;但是不需要暴露这个聚合物的内部表述方式核心思想&#xff1a;将容器和算法分开&#xff0c;彼此独立设计容器和算法的泛型化&#xff0c;均可以使用模板&#xff0c;使用迭代器连接容器和算法例子 templ…

STL源码剖析 5中迭代器型别

最常使用的5种迭代器的型别 为 value_type、difference_type、pointer、reference、iterator_category。如果想要自己开发的容器和STL进行适配&#xff0c;就需要定义上述5种类型 iteraor_traits 必须针对传入的型别为 pointer 或者 pointer-to-const设计偏特化版本 template &…

加载tf模型 正确率很低_深度学习模型训练全流程!

↑↑↑关注后"星标"Datawhale每日干货 & 每月组队学习&#xff0c;不错过Datawhale干货 作者&#xff1a;黄星源、奉现&#xff0c;Datawhale优秀学习者本文从构建数据验证集、模型训练、模型加载和模型调参四个部分对深度学习中模型训练的全流程进行讲解。一个成…

Python学习17 Turtle库绘图

学习网址&#xff1a;https://docs.python.org/zh-cn/3/library/turtle.html Turtle库 Turtle库是Python语言中一个很流行的绘制图像的函数库&#xff0c;一个小乌龟&#xff0c;在一个横轴为x、纵轴为y的坐标系原点&#xff08;画布中心&#xff09;&#xff0c;(0,0)位置开…

android ros 节点编写_嵌入式的我们为什么要学ROS

前言本来是要写一篇STM32移植ROS的一个小lib库&#xff0c;ROS一般都是需要跑在Linux上的&#xff0c;STM32使用就是当成一个ROS通讯的小节点&#xff0c;但是写文章时间不够&#xff0c;所以就简单做一篇ROS的介绍文章&#xff0c;分享给嵌入式的小伙伴们。ROS现在在机器人领域…

STL源码剖析 __type_traits

traits编程 弥补了C本身的不足STL只对迭代器进行规范制定出了iterator_traits&#xff0c;SGI在此基础上进一步扩展&#xff0c;产生了__type_traits双下划线的含义是这个是SGI内部使用的东西&#xff0c;不属于STL标准iterator_traits 负责萃取迭代器的特性__type_traits负责萃…

STL源码剖析 序列式容器|Vector

容器的概观和分类 array 数组 、list 链表、tree树 、stack堆栈、queue队列、hash table散列表、set集合、map映射表根据数据在容器中的排列顺序&#xff0c;将上述数据结构分为序列式和关联式两种类型SGI STL使用内缩方式来表达基层和衍生层之间的关系衍生不是派生&#xff0…

ansible 修改文件变量_Ansible Playbook中的变量与引用

Ansible是一个系列文章&#xff0c;我会尽量以通俗易懂、诙谐幽默的总结方式给大家呈现这些枯燥的知识点&#xff0c;让学习变的有趣一些。Ansible自动化运维前言前面有说到使用playbook来搞一些复杂的功能&#xff0c;我们使用YAML来写playbook&#xff0c;就像我们用其它语言…

STL源码剖析 list概述

目录 list的节点(node) list迭代器 list 的构造和内存管理 list 的元素操作 list相较于vector连续的线性空间就显得很复杂&#xff0c;他的存储空间是不连续的&#xff0c;好处是每次插入和删除一个元素的时候&#xff0c;只需要配置或者释放一个元素的空间 插入和删除十分的…

vsftp不允许切换到其它目录_IntelliJ IDEA如何对project的目录进行筛选显示?

如果你的项目很庞大&#xff0c;同一个功能用到的各种文件散落在多个文件夹&#xff0c;开发时切换不便&#xff0c;可以利用scope功能&#xff0c;只显示该功能用到的文件&#xff0c;让project列表十分清爽&#xff0c;提高开发效率。本文使用的IDEA版本为2020.1。1、打开sco…

密码学专题 对称加密算法

一般来说&#xff0c;使用OpenSSL对称加密算法有两种方式&#xff0c;一种是使用API函数的方式&#xff0c;一种是使用OpenSSL提供的对称加密算法指令方式。本书将介绍对称加密算法的指令方式OpenSSL的对称加密算法指令主要用来对数据进行加密和解密处理&#xff0c;输入输出的…

网络防火墙单向和双向_单向晶闸管与双向晶闸管之间的不同之处

晶闸管是回一个可以控导点开关&#xff0c;能以弱电去控制强电的各种电路。晶闸管常用于整流&#xff0c;调压&#xff0c;交直流变化&#xff0c;开关&#xff0c;调光等控制电路中。具有提交小&#xff0c;重量轻&#xff0c;耐压高&#xff0c;容量大&#xff0c;效率高&…

python版本切换_怎么切换python版本

展开全部 &#xff08;1&#xff09;分别安2113装 python-2.7.12.amd64.msi python-3.5.2-amd64.exe &#xff08;python官网下载的&#xff09; 顺序无所谓&#xff08;为5261了看着4102方便&#xff0c;我把安装路径修改统一了1653&#xff09; &#xff08;2&#xff09;配置…

react.lazy 路由懒加载_Vue面试题: 如何实现路由懒加载?

非懒加载import List from /components/list.vue const router new VueRouter({routes: [{ path: /list, component: List }] })方案一(常用)const List () > import(/components/list.vue) const router new VueRouter({routes: [{ path: /list, component: List }] })方…

STL源码剖析 deque双端队列 概述

vector是单向开口的连续线性空间&#xff0c;deque是一种双向开口的连续线性空间。deque可以在头尾两端分别进行元素的插入和删除操作vector和deque的差异 1&#xff0c;deque允许常数时间内对于头端元素进行插入和删除操作2&#xff0c;deque没有所谓容量(capacity)的概念&…

STL源码剖析 stack 栈 概述->(使用deque双端队列 / list链表)作为stack的底层容器

Stack是一种先进后出的数据结构&#xff0c;他只有一个出口stack允许 新增元素、移除元素、取得最顶端的元素&#xff0c;但是无法获得stack的内部数据&#xff0c;因此satck没有遍历行为Stack定义的完整列表 (双端队列作为Stack的底层容器) 将deque作为Stack的底部结构&#…

python怎么实现图像去噪_基于深度卷积神经网络和跳跃连接的图像去噪和超分辨...

Image Restoration Using Very Deep Convolutional Encoder-Decoder Networks with Symmetric Skip Connections作者&#xff1a;Xiao-Jiao Mao、Chunhua Shen等本文提出了一个深度的全卷积编码-解码框架来解决去噪和超分辨之类的图像修复问题。网络由多层的卷积和反卷积组成&a…