STL中的内存分配方式——3

    技术2022-05-19  20

    前面说到了如果需要的内存空间大于128bytes就是用第一级配置器,那么现在就来看下,如果需要的内存空间小于128bytes的分配方法:

    第二级配置器。

    在书中侯捷分析了,对于空间小于128bytes的,把空间分成16种类型的小额区间:8bytes,16bytes,24bytes,32bytes,.....128bytes。如果需要10bytes那么就在16bytes这种类型中取,如果需要n  bytes那么就在(n+8-1)&(~7) bytes这种类型里面去。

    那么这16中类型的小额区间分别是怎么保存地址信息的呢,它是通过一个联合体:

    union obj{

     union obj* free_list_link;

     char client_date[1];

    };

    通过free_list_link指针指向下一个空余空间,同时因为free_list_link是一个obj指针,所以通过这个指针又可以指向下一个空余空间。

    这就相当于一个栈,栈中的每个元素都对应一段空余空间。但是它并没有单独分配空间来保存这些地址,而只是保存了一个头指针,通过这个头指针来访问这些空间。

    那么既然我们有16种类型的小区间,那么我们就有16个“栈”。

    当我们需要分配一段区间时,首先我们把这段区间分类,看它是哪种类型的,然后在这种类型的“栈”中取出栈头节点,那么这个栈头节点的地址就是我们分配到的空间地址了。

    然后问题来了,我们怎么知道这些栈头的地址呢,在STL中,它通过一个数组来保存了这些地址:

     static obj* free_list[__NFREELISTS];

    下面来看一段代码:

    template<bool threads,int inst> void* __my_default_alloc_template<threads,inst>::allocate (size_t n) { if(n>128) return __my_malloc_alloc_template<inst>::allocate(n); else { size_t index=FREE_LIST_INDEX(n); obj* result; result=free_list[index]; if(0!=result) { free_list[index]=result->free_list_link; return result; } else { result=(obj*)refill(n); return result; } } }

    在这段代码中 我们可以看到当需要的空间大于128的时候就直接调用第一级配置器去分配空间,其实就是调用malloc来分配,

    然后但空间小于128的时候:

    首先是:size_t index=FREE_LIST_INDEX(n);

    得到我们需要空间的类型。

    然后:result=free_list[index];

    这就是“栈头”地址,也就是我们需要返回的地址。

    当然 最后要重新把栈头地址 指向下一个空余空间:

    free_list[index]=result->free_list_link;

    上面的就是基本的思想,但是我们一开始怎么得到空余空间呢,或者说但我们某一类型的空余空间用完了该怎么办呢:

    在STL中,它有一个内存池,通过两个指针指向这段内存:

    private: static void *start_free;//内存池的开始位置 static void *end_free;//内存池的结尾位置

    这段内存池是通过malloc或得到的,但是这不是重点重点是,如果我们需要 total_bytes大小的空间但是我们剩下的空间bytes_left不足以满足它怎么办:

    STL中分了两部:

    第一步:

    吧剩下的空间bytes_left先配给适当的类型空间:

    size_t index=FREE_LIST_INDEX(bytes_left); ((obj*)start_free)->free_list_link=free_list[index]; free_list[index]=(obj*)start_free;

    第二步:

    通过malloc来分配空间,如果分配失败,那么就从其它类型空间分区里面调节:

    for(i=n;i<__MAX_BYTES;i+=__ALIGN) { size_t index=FREE_LIST_INDEX(i); p=free_list[index]; if(0!=p) { start_free=(void*)p; end_free=(char*)start_free+i; free_list[index]=p->free_list_link; return chunk_alloc(n,nobjs); } }

    主要的思想就是这上面了。

    然后把完整的代码按照书上面的内容自己写了遍:

     enum{__ALIGN=8};//小型区块的上调上界 enum{__MAX_BYTES=128};//小型区块的上限 enum{__NFREELISTS=__MAX_BYTES/__ALIGN};//free-list 的个数 template<bool threads,int inst> class __my_default_alloc_template { private: static void *start_free;//内存池的开始位置 static void *end_free;//内存池的结尾位置 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);//重新分配空间 private: typedef union OBJ { OBJ* free_list_link; char client_data[1]; } obj; static obj* free_list[__NFREELISTS]; static size_t FREE_LIST_INDEX(size_t n); static void* refill(size_t n); static char* chunk_alloc(size_t size,int &nobjs); static size_t ROUND_UP(size_t n) { return (n+__ALIGN)&~(__ALIGN-1); } }; template<bool threads,int inst> void* __my_default_alloc_template<threads,inst>::start_free=(void*)0; template<bool threads,int inst> void* __my_default_alloc_template<threads,inst>::end_free =(void*)0; template<bool threads,int inst> size_t __my_default_alloc_template<threads,inst>::heap_size =0; template<bool threads,int inst> typename __my_default_alloc_template<threads,inst>::obj * __my_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> size_t __my_default_alloc_template<threads,inst>::FREE_LIST_INDEX (size_t n) { return (n+__ALIGN-1)/__ALIGN-1; } template<bool threads,int inst> void* __my_default_alloc_template<threads,inst>::allocate (size_t n) { if(n>128) return __my_malloc_alloc_template<inst>::allocate(n); else { size_t index=FREE_LIST_INDEX(n); obj* result; result=free_list[index]; if(0!=result) { free_list[index]=result->free_list_link; return result; } else { result=(obj*)refill(n); return result; } } } template<bool threads,int inst> void __my_default_alloc_template<threads,inst>::deallocate (void *p,size_t n) { if(n>128) return __my_malloc_alloc_template<inst>::deallocate (p,n); else if(n>0) { size_t index=FREE_LIST_INDEX(n); ((obj*)p)->free_list_link=free_list[index]; free_list[index]=(obj*)p; } } template<bool threads,int inst> void* __my_default_alloc_template<threads,inst>::reallocate (void *p,size_t old_sz,size_t new_sz) { if(old_sz>128&&new_sz>128) return __my_malloc_alloc_template<inet>::reallocate (p,old_sz,new_sz); else if(old_sz>128&&new_sz<=128) { __my_malloc_alloc_template<inet>::deallocate (p,old_sz); return allocate(new_sz); } else if(old_sz<=128&&new_sz>128) { deallocate(p,old_sz); return __my_malloc_alloc_template<int>::allocate (new_sz); } else { deallocate(p,old_sz); return allocate(new_sz); } } template<bool threads,int inst> void* __my_default_alloc_template<threads,inst>::refill(size_t n) { n=ROUND_UP(n); int nobjs=20; char* chunk=chunk_alloc(n,nobjs); if(1==nobjs) return chunk; char* result; result=chunk; size_t index=FREE_LIST_INDEX(n); free_list[index]=(obj*)(chunk+n); for(int i=1;i<nobjs-1;++i) { ((obj*)(chunk+i*n))->free_list_link=(obj*)(chunk+(i+1)*n); } ((obj*)(chunk+(nobjs-1)*n))->free_list_link=0; return result; } template<bool threads,int inst> char* __my_default_alloc_template<threads,inst>::chunk_alloc (size_t n,int & nobjs) { char* result; size_t total_bytes=n*nobjs; size_t bytes_left=(char*)end_free-(char*)start_free; if(bytes_left>=total_bytes) { result=(char*)start_free; start_free=(char*)start_free+total_bytes; return result; } else if(bytes_left>=n) { nobjs=bytes_left/n; result=(char*)start_free; start_free=(char*)start_free+nobjs*n; return result; } else { if(bytes_left>0) { size_t index=FREE_LIST_INDEX(bytes_left); ((obj*)start_free)->free_list_link=free_list[index]; free_list[index]=(obj*)start_free; } size_t bytes_to_get=2*total_bytes+ROUND_UP(heap_size>>4); start_free=malloc(bytes_to_get); obj* p; if(0==start_free) { int i; for(i=n;i<__MAX_BYTES;i+=__ALIGN) { size_t index=FREE_LIST_INDEX(i); p=free_list[index]; if(0!=p) { start_free=(void*)p; end_free=(char*)start_free+i; free_list[index]=p->free_list_link; return chunk_alloc(n,nobjs); } } } heap_size+=bytes_to_get; end_free=(char*)start_free+bytes_to_get; return chunk_alloc(n,nobjs); } }; template<typename T> struct my_iterator_traits { typedef typename T::value_type value_type; typedef typename T::difference_type difference_type; typedef typename T::pointer pointer; typedef typename T::reference reference; typedef typename T::iterator_category iterator_category; }; template<typename T> struct my_iterator_traits<T*> { typedef T value_type; typedef ptrdiff_t difference_type; typedef T* pointer; typedef T& reference; typedef typename std::random_access_iterator_tag iterator_category; }; template<typename T> struct my_iterator_traits<const T*> { typedef T value_type; typedef ptrdiff_t difference_type; typedef const T* pointer; typedef const T& reference; typedef typename std::random_access_iterator_tag iterator_category; };


    最新回复(0)