SGI特殊的空间配置器,std::alloc
作者:互联网
构造和析构基本工具:construct()和destroy()
下面是<stl_construct.h>的部分内容
#include<new.h> //欲使用placement new,需先包含此文件
template<class T1,class T2>
inline void construct(T1 *p,const T2& value){
new (p) T1(value);//placement new;调用T1::T1(value);
}
//以下是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(ForwardItrator 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(ForwardItrator first,ForwardIterator last,__false_type){
for(;first<last;++first)
destroy(&*first);
}
//如果元素的数值型别(value_type)有trivial destructor...
template<class ForwardIterator>
inline void __destroy(ForwardItrator first,ForwardIterator last,__true_type){}
//以下是destroy()第二版本针对迭代器为char*和wchar*的特化版
inline void destroy(char*,char*){}
inline void destroy(wchar*,wchar*){}
上述的construct()接受一个指针p和一个初值value,该函数的用途就是将初值设定到指针所指的空间上。C++的placment new运算子(即定位new)可用来完成这一任务。
分配器std::alloc
下图是std::alloc内存的组织形式:
一级分配器
其中my_malloc_handler函数,是由用户注册的,在内存分配到山穷水尽,无法再分配所需的可用大小的空间时,会调用这个函数,尝试能不能改善内存情况。如果"内存不足处理例程"并未被客端设定,oom_malloc()和oom_realloc()便调用__THROW_BAD_ALLOC丢出bad_alloc异常信息或利用exit(1)硬生生终止程序。
功能:
1.allocate()直接使用malloc();deallocate()直接使用free().
2.模拟C++的set_new_handler()以处理内存不足的状况
#include <stdlib.h> //for malloc(),realloc()
#include <stddef.h> //for size_t
#include <memory.h> //for memcpy()
//#define __THROW_BAD_ALLOC cerr << "out of memory" << endl; exit(1)
#define __THROW_BAD_ALLOC exit(1)
//----------------------------------------------
// 第1級配置器。
//----------------------------------------------
void (*oom_handler)() = 0;
void* oom_malloc(size_t n)
{
void (*my_malloc_handler)();
void* result;
for (;;) { //不斷嘗試釋放、配置、再釋放、再配置…
my_malloc_handler = oom_handler;
if (0 == my_malloc_handler) { __THROW_BAD_ALLOC; }
(*my_malloc_handler)(); //呼叫處理常式,企圖釋放記憶體
result = malloc(n); //再次嘗試配置記憶體
if (result) return(result);
}
}
void* oom_realloc(void *p, size_t n)
{
void (*my_malloc_handler)();
void* result;
for (;;) { //不斷嘗試釋放、配置、再釋放、再配置…
my_malloc_handler = oom_handler;
if (0 == my_malloc_handler) { __THROW_BAD_ALLOC; }
(*my_malloc_handler)(); //呼叫處理常式,企圖釋放記憶體。
result = realloc(p, n); //再次嘗試配置記憶體。
if (result) return(result);
}
}
void* malloc_allocate(size_t n)
{
void *result = malloc(n); //直接使用 malloc()
if (0 == result) result = oom_malloc(n);
return result;
}
void malloc_deallocate(void* p, size_t n)
{
free(p); //直接使用 free()
}
void* malloc_reallocate(void *p, size_t old_sz, size_t new_sz)
{
void* result = realloc(p, new_sz); //直接使用 realloc()
if (0 == result) result = oom_realloc(p, new_sz);
return result;
}
void (*set_malloc_handler(void (*f)()))()
{ //類似 C++ 的 set_new_handler().
void (*old)() = oom_handler;
oom_handler = f;
return(old);
}
二级配置器
1.维护16个自由链表(申请对应大小内存块时,才初始化链表),负责16种小型区块的次配置能力。内存池以malloc配置而得。如果内存不足,转调用第一级配置器(那里有处理程序)。
2.如果需求区块大于128bytes,就转调用第一级配置器
//----------------------------------------------
//第二級配置器
//----------------------------------------------
enum {__ALIGN = 8}; //小區塊的上調邊界
enum {__MAX_BYTES = 128}; //小區塊的上限
enum {__NFREELISTS = __MAX_BYTES/__ALIGN}; //free-lists 個數
// union obj { //G291[o],CB5[x],VC6[x]
// union obj* free_list_link; //這麼寫在 VC6 和 CB5 中也可以,
// }; //但以後就得使用 "union obj" 而不能只寫 "obj"
typedef struct __obj {
struct __obj* free_list_link;
} obj;
//内存池起始位置,只在chunk_alloc()中变化
char* start_free = 0;
//内存池结束位置,只在chunk_alloc()中变化
char* end_free = 0;
//已申请的空间大小
size_t heap_size = 0;
//obj数组,每个元素一个指针,都指向一条链表
obj* free_list[__NFREELISTS]
= {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
//小区块上调边界,8字节对齐
size_t ROUND_UP(size_t bytes) {
return (((bytes) + __ALIGN-1) & ~(__ALIGN - 1));
}
//以下函数根据区块大小,决定使用第n号free-list。n从0开始计算
size_t FREELIST_INDEX(size_t bytes) {
return (((bytes) + __ALIGN-1)/__ALIGN - 1);
}
//----------------------------------------------
// We allocate memory in large chunks in order to
// avoid fragmentingthe malloc heap too much.
// We assume that size is properly aligned.
//
// Allocates a chunk for nobjs of size "size".
// nobjs may be reduced if it is inconvenient to
// allocate the requested number.
//我们将内存分配成大块,以便
//避免过多地分割malloc堆。
//我们假设大小正确对齐。
//
//为大小为“size”的nobj分配块。
//如果不方便使用,NOBJ可能会减少
//分配请求的号码。
//----------------------------------------------
//char* chunk_alloc(size_t size, int& nobjs) //G291[o],VC6[x],CB5[x]
//配置一大块空间的大小,可容纳nobjs个大小为"size"的区块
//如果配置nobjs个区块有所不便,nobjs可能会降低
char* chunk_alloc(size_t size, int* nobjs)
{
char* result;
//期望获取的空间大小
size_t total_bytes = size * (*nobjs); //原 nobjs 改為 (*nobjs)
//内存池剩余空间大小
size_t bytes_left = end_free - start_free;
if (bytes_left >= total_bytes) {
//内存池剩余空间完全满足需求量
result = start_free;
start_free += total_bytes;
return(result);
} else if (bytes_left >= size) {
//内存池剩余空间不能完全满足需求量,但足够供应一个(含)以上的区块
//返回时间获得的区块数量
*nobjs = bytes_left / size; //原 nobjs 改為 (*nobjs)
total_bytes = size * (*nobjs); //原 nobjs 改為 (*nobjs)
result = start_free;
start_free += total_bytes;
return(result);
} else {
//内存池剩余空间连一个区块的大小都无法提供
//计算配置新空间的大小
size_t bytes_to_get =
2 * total_bytes + ROUND_UP(heap_size >> 4);
// Try to make use of the left-over piece.
//以下尝试让内存池中残余零头还有利用价值
if (bytes_left > 0) {
//内存池还有一下零头,先配给适当的free list
obj* volatile *my_free_list =
free_list + FREELIST_INDEX(bytes_left);
//调整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) {
//malloc失败
int i;
obj* volatile *my_free_list, *p;
//Try to make do with what we have. That can't
//hurt. We do not try smaller requests, since that tends
//to result in disaster on multi-process machines.
//试着检视我们手上拥有的东西,这不会造成伤害。我们不打算尝试配置
//较小的区块,因为那在多进程()机器上容易导致灾难
//以下搜寻适当的free list
//所谓适当是指"尚有未用区块,且区块足够大"之free list
for (i = size; i <= __MAX_BYTES; i += __ALIGN) {
my_free_list = free_list + FREELIST_INDEX(i);
p = *my_free_list;
if (0 != p) {//free list内尚有未用区块
//调整free list以释放未用区块
//即相当于从当前区块大于size的区块且还存在可用区块的区块,借一块区块
*my_free_list = p -> free_list_link;
//重置剩余空间区间
start_free = (char*)p;
end_free = start_free + i;
//递归调用自己,为了修正nobjs
return(chunk_alloc(size, nobjs));
//注意,任何残余零头终将被编入适当的free-list中备用
//Any leftover piece will eventually make it to the
//right free list.
}
}
//如果真的山穷水尽,真的没得可以借用的
end_free = 0; //In case of exception.
//就调用第一级配置器,看看能不能通过设置的函数释放出来一些空间,如果不能会导致抛出异常
start_free = (char*)malloc_allocate(bytes_to_get);
//This should either throw an exception or
//remedy the situation. Thus we assume it
//succeeded.
}
//成功从第一级配置,得到了内存改善
heap_size += bytes_to_get;
end_free = start_free + bytes_to_get;
//递归调用自己,为了修正nobjs
return(chunk_alloc(size, nobjs));
}
}
//----------------------------------------------
// Returns an object of size n, and optionally adds
// to size n free list. We assume that n is properly aligned.
// We hold the allocation lock.
//----------------------------------------------
//返回一个大小为n的对象,并可能加入大小wein的其它区块到free list
//n已上调到8的倍数
void* refill(size_t n)
{
int nobjs = 20;
//调用chunk_alloc(),尝试取得nobjs个区块作为free-list的新节点
//注意参数nobjs是指针
char* chunk = chunk_alloc(n,&nobjs);
obj* volatile *my_free_list; //obj** my_free_list;
obj* result;
obj* current_obj;
obj* next_obj;
int i;
//如果之获得一个区块,这个区块就分配给调用者用,free list无新节点
if (1 == nobjs) return(chunk);
//否则准备调整free list,纳入新节点
my_free_list = free_list + FREELIST_INDEX(n);
//Build free list in chunk
//以下在chunk空间内建立free list,这一块准备返回给客户端
result = (obj*)chunk;
//以下导引free list指向新配置的空间(取自内存池)
*my_free_list = next_obj = (obj*)(chunk + n);
//以下将free list的各个节点串接起来,从1开始,因为第0个将返回给客户端
for (i=1; ; ++i) {
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);
}
//----------------------------------------------
//
//----------------------------------------------
void* allocate(size_t n) //n must be > 0
{
obj* volatile *my_free_list; //obj** my_free_list;
obj* result;
//先判断区块大小,大于128byte就调用第一级配置器
if (n > (size_t)__MAX_BYTES) {
return(malloc_allocate(n));
}
//获取这个区块大小在内存池的位置
my_free_list = free_list + FREELIST_INDEX(n);
result = *my_free_list;
//如果这个节点链表的为NULL
if (result == 0) {
void* r = refill(ROUND_UP(n));
return r;
}
//更新链表位置
*my_free_list = result->free_list_link;
return (result);
}
//----------------------------------------------
//
//----------------------------------------------
void deallocate(void *p, size_t n) //p may not be 0
{
obj* q = (obj*)p;
obj* volatile *my_free_list; //obj** my_free_list;
if (n > (size_t) __MAX_BYTES) {
malloc_deallocate(p, n);
return;
}
my_free_list = free_list + FREELIST_INDEX(n);
q->free_list_link = *my_free_list;
*my_free_list = q;
}
//----------------------------------------------
//
//----------------------------------------------
void* reallocate(void *p, size_t old_sz, size_t new_sz)
{
void * result;
size_t copy_sz;
if (old_sz > (size_t) __MAX_BYTES && new_sz > (size_t) __MAX_BYTES) {
return(realloc(p, new_sz));
}
if (ROUND_UP(old_sz) == ROUND_UP(new_sz)) return(p);
result = allocate(new_sz);
copy_sz = new_sz > old_sz? old_sz : new_sz;
memcpy(result, p, copy_sz);
deallocate(p, old_sz);
return(result);
}
//----------------------------------------------
来源:STL源码刨析
标签:std,alloc,obj,void,list,free,SGI,result,size 来源: https://blog.csdn.net/weixin_45673259/article/details/123618163