您的位置:首页 > 其它

SGI_STL_空间配置器

2012-09-10 12:59 330 查看
一般而言,我们习惯C++内存申请操作和释放操作如下:

class Foo{};

Foo * pf = new Foo; //1.申请空间2.调用构造函数

delete pf; //1.析构函数2.释放空间

但是为了精确分工,STL Allocator这两个阶段分开来。PS:以下代码不讨论多线程情况

内存申请:alloc::allocate()负责。

内存释放:alloc::deallocate()负责。

对象构造:全局 ::construct()负责。

对象析构:全局 ::destroy()负责。



1.<stl_construct.h> 对象构造和析构关键代码

【::construct()和::destroy()被设计为全局函数。::destory()有2个版本,第一个版本接受一个指针,调用该析构函数即可,第二个版本考虑到大范围的对象析构可能无关痛痒(trivial destructor),使用value_type()获得迭代器所指对象的型别,再利用__tyep_traits<T>判断trivial?不做:调用第一个版本】

#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(value);
}

// 以下是destroy() 第一版本,接受一个对象指针。
template <class T>
inline void destroy(T* pointer)
{
pointer->~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
//循环调用destroy一个版本
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*) {}


2.<stl_alloc.h> 空间的申请和释放

【C++内存申请基本操作:::operator new(),内存释放基本操作:::operator delete()。SGI用malloc和free这2个C函数代替完成内存的申请和释放。考虑到内存碎片问题,SGI设计了两级配置器,第一级直接使用malloc和free,包括了内存不足处理机制,需要调用者自己提供。第二级判断配置区超过128字节,调用第一级配置器。小于128字节,采用内存池管理】





2.1 配置器接口

//简单的转调用,可能第一级或者第二级配置器

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


2.2 vector简单使用例子

template<class T, class Alloc>
class vector
{
protected:
typedef simple_alloc<value_type,Alloc> data_allocator;

void deallocate()
{
if(...)
data_allocator::deallocate(start,end_of_storage - start);
}
};


2.3 第一级配置器

【SGI第一级配置器的allocate和realloc都是在调用C函数malloc和realloc不成功后,改调用oom_malloc和oom_realloc,后两者都有内循环,不断调用你指定的内存不足处理函数】

// Malloc-based allocator.  Typically slower than default alloc below.
// Typically thread-safe and more storage efficient.
#ifdef __STL_STATIC_TEMPLATE_MEMBER_BUG
# ifdef __DECLARE_GLOBALS_HERE
void (* __malloc_alloc_oom_handler)() = 0;
// g++ 2.7.2 does not handle static template data members.
# else
extern void (* __malloc_alloc_oom_handler)();
# endif
#endif

//inst没排上用场
template <int inst>
class __malloc_alloc_template
{
private:

//处理内存不足
static void *oom_malloc(size_t);
static void *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)
{
//第一级直接用malloc;
void *result = malloc(n);
//malloc内存不足补救函数
if (0 == result)
result = oom_malloc(n);
return result;
}

static void deallocate(void *p, size_t /* n */)
{
//直接free
free(p);
}

static void * reallocate(void *p, size_t /* old_sz */, size_t new_sz)
{
//直接使用realloc 下面为补救函数
void * result = realloc(p, new_sz);
if (0 == result)
result = oom_realloc(p, new_sz);
return result;
}

static void (* set_malloc_handler(void (*f)()))()
{
//指定你的out-of-memory处理函数
void (* old)() = __malloc_alloc_oom_handler;
__malloc_alloc_oom_handler = f;
return(old);
}
};

// malloc_alloc out-of-memory handling

#ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG
template <int inst>
//你的out-of-memory处理函数初始为
void (* __malloc_alloc_template<inst>::__malloc_alloc_oom_handler)() = 0;
#endif

template <int inst>
void * __malloc_alloc_template<inst>::oom_malloc(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, 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 = realloc(p, n);
if (result)
return(result);
}
}


2.4 第二级配置器

【每次先申请一大块内存作为内存池,并且维护16条链表,分别从8-16-24-32……-128字节(配置器会主动将当前内存申请提升到8的倍数),链表负责配置出去(配置出去时链表不再指向)和回收回来(回收回来时链表重新指向),当链表空间不足时向内存池申请。】

2.4.1 链表节点结构

//利用union的性质使得链表既是指针又是实际区块
union obj
{
union obj * free_list_link;
char client_data[1];       //柔性数组
};




2.4.2 配置器类内代码

enum {__ALIGN = 8};
enum {__MAX_BYTES = 128};
enum {__NFREELISTS = __MAX_BYTES/__ALIGN};
#endif

template <bool threads, int inst>
class __default_alloc_template {

private:
//扩展到__ALIGN的倍数也即是的倍数
static size_t ROUND_UP(size_t bytes)
{
return (((bytes) + __ALIGN-1) & ~(__ALIGN - 1));
}
private:
union obj
{
union obj * free_list_link;
char client_data[1];    /* The client sees this.        */
};
private:
//16条链表
static obj *  free_list[__NFREELISTS];

//根据申请的大小快速找到所属链表
static  size_t FREELIST_INDEX(size_t bytes)
{
return (((bytes) + __ALIGN-1)/__ALIGN - 1);
}

//向内存池申请n大小的区间n为的倍数
static void *refill(size_t n);

//内存池成处理申请函数一般为一次性配置nobjs个size大小的块nobjs默认为
//不足时,nobjs返回不定数目
static char *chunk_alloc(size_t size, int &nobjs);

//内存池起始位置只在chunk_alloc变化
static char *start_free;
//内存池结束位置只在chunk_alloc变化
static char *end_free;
//内存池heap_size
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);

} ;


2.4.3 allocate()实现

/* n must be > 0      */
static void * allocate(size_t n)
{
obj * __VOLATILE * my_free_list;
obj * __RESTRICT result;

//大于字节就调用第一级配置器
if (n > (size_t) __MAX_BYTES)
{
return(malloc_alloc::allocate(n));
}

//找到链表中的所属链表
my_free_list = free_list + FREELIST_INDEX(n);

result = *my_free_list;
//链表没有可用的块调用refill申请
if (result == 0)
{
void *r = refill(ROUND_UP(n));
return r;
}
//调整链表不再指向这块被使用的块
*my_free_list = result -> free_list_link;
return (result);
};


2.4.4 deallocate()实现

/* p may not be 0 */
static void deallocate(void *p, size_t n)
{
obj *q = (obj *)p;
obj * __VOLATILE * my_free_list;

//大于使用第一级配置器
if (n > (size_t) __MAX_BYTES)
{
malloc_alloc::deallocate(p, n);
return;
}
//找到所属链表
my_free_list = free_list + FREELIST_INDEX(n);

//调整链表回收区块
q -> free_list_link = *my_free_list;
//链表重新指向回收块
*my_free_list = q;

}


2.4.5 refill()实现

void* __default_alloc_template<threads, inst>::refill(size_t n)
{
//默认申请块数
int nobjs = 20;
//nobjs为传引用
char * chunk = chunk_alloc(n, nobjs);
obj * __VOLATILE * my_free_list;
obj * result;
obj * current_obj, * next_obj;
int i;

//只有一块返回给调用者链表无新节点
if (1 == nobjs)
return(chunk);

//不止一块找到所属链表
my_free_list = free_list + FREELIST_INDEX(n);

//对新的区块建链表
result = (obj *)chunk;
//链表指向从内存池拿出的链表
*my_free_list = next_obj = (obj *)(chunk + n);
//将新链表之间的节点串接起来
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);   //返回给申请者
}


2.4.6 内存池chunk_alloc()实现

_default_alloc_template<threads, inst>::chunk_alloc(size_t size, int& nobjs)
{
char * result;
//申请总量
size_t total_bytes = size * nobjs;
//内存池余额
size_t bytes_left = end_free - start_free;

//余额满足申请总量
if (bytes_left >= total_bytes)
{
//返回result
result = start_free;
//内存池可用起始地址+=申请总量
start_free += total_bytes;
return(result);
}
//不足以满足申请总量但是可以供应一个以上区块
else if (bytes_left >= size)
{
//重新得到能够供应个数
nobjs = bytes_left/size;
//重新计算总量
total_bytes = size * nobjs;
//同上
result = start_free;
start_free += total_bytes;
return(result);
}
//一个区块都提供不了
else
{

size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);

//余额大于把这些零头凑到链表里去
if (bytes_left > 0)
{
obj * __VOLATILE * my_free_list = free_list + FREELIST_INDEX(bytes_left);

((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)
{
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.

//以下搜索适当的链表
//适当是指"尚有未用区块的链表"
for (i = size; i <= __MAX_BYTES; i += __ALIGN)
{
//拿到这个链表
my_free_list = free_list + FREELIST_INDEX(i);
p = *my_free_list;
//
if (0 != p)
{
//调整链表释放未使用区块
//链表起始指向下一个区块
*my_free_list = p -> free_list_link;
//内存池开头指向这个区块
start_free = (char *)p;
//结尾指向区块结尾
end_free = start_free + i;
//再一次调用自身
return(chunk_alloc(size, nobjs));
// Any leftover piece will eventually make it to the
// right free list.
}
}
//没办法没内存用了
end_free = 0;    // In case of exception.
//调用第一级配置器看看out-of-memory机制能否出点力
start_free = (char *)malloc_alloc::allocate(bytes_to_get);
// This should either throw an exception
// or remedy the situation.  Thus we assume it
// succeeded.
}
//heap_size+=
heap_size += bytes_to_get;
//结束位置重新界定
end_free = start_free + bytes_to_get;
return(chunk_alloc(size, nobjs));
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: