您的位置:首页 > 其它

自己动手实现STL 01:内存配置器的实现(stl_alloc.h)

2014-11-17 21:05 316 查看

一、前言

  在STL中,容器是其中的重中之重,基本的STL中的算法,仿函数等都是围绕着容器实现的功能。而,内存配置器,是容器的实现的基础。所以,我第一次要去编写便是内存配置器的实现。在STL中,内存配置器的实现是在stl_alloc.h中。

二、配置器原理简要介绍

  在SGI STL中配置分为两级,第一级配置器和第二级配置器。两者关系如下:

/*************************************************************************
> File Name: stl_alloc_wjzh.h
> Author: wjzh
> Mail: wangjzh_1@163.com
> Created Time: 2014年10月31日 星期五 16时06分23秒
************************************************************************/

#ifndef __SGI_STL_INTERNAL_ALLOC_WJZH_H
#define __SGI_STL_INTERNAL_ALLOC_WJZH_H

#ifdef __SUNPRO_CC
#    define __PRIVATE public
#else
#    define __PRIVATE private
#endif

#ifdef __STL_STATIC_TEMPLATE_MEMBER_BUG
#    define __USE_MALLOC
#endif

#if 0
#    include <new>
#    define __THROW_BAD_ALLOC throw bad_alloc
#elif !defined(__THROW_BAD_ALLOC)
//#    include <iostream.h>
#include <iostream>
#    define __THROW_BAD_ALLOC std::cerr << "out of memory" << std::endl; exit(1)
#endif

#ifndef __ALLOC
#    define __ALLOC alloc
#endif
#ifdef __STL_WIN32THREADS
#    include <windows.h>
#endif

#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#ifndef __RESTRICT
#    define __RESTRICT
#endif

#if !defined(__STL_PTHREADS) && !defined(_NOTHREADS) \
&& !defined(__STL_SGI_THREADS) && !defined(__STL_WIN32THREADS)
#    define _NOTHREADS
#endif

#ifdef __STL_PTHREADS
#    include <pthread.h>
#    define __NODE_ALLOCATOR_LOCK \
if (threads) pthread_mutex_lock(&__node_allocator_lock)
#    define __NODE_ALLOCATOR_UNLOCK \
if (threads) pthread_mutex_unlock(&__node_allocator_lock)
#    define __NODE_ALLOCATOR_THREADS true
#    define __VOLATILE volatile
# endif
# ifdef __STL_WIN32THREADS
#    define __NODE_ALLOCATOR_LOCK \
EnterCriticalSection(&__node_allocator_lock)
#    define __NODE_ALLOCATOR_UNLOCK \
LeaveCriticalSection(&__node_allocator_lock)
#    define __NODE_ALLOCATOR_THREADS true
#    define __VOLATILE volatile
# endif

# ifdef __STL_SGI_THREADS
extern "C" {
extern int __us_rsthread_malloc;
}
#    define __NODE_ALLOCATOR_LOCK if (threads && __us_rsthread_malloc) \
{ __lock(&__node_allocator_lock);    }
#    define __NODE_ALLOCATOR_UNLOCK if(threads && __us_rsthread_malloc) \
{ __unlcok(&__node_allocator_lock);    }
#    define __NODE_ALLOCATOR_THREADS true
#    define __VOLATILE volatile
# endif

# ifdef _NOTHREADS
#    define __NODE_ALLOCATOR_LOCK
#    define __NODE_ALLOCATOR_UNLOCK
#    define __NODE_ALLOCATOR_THREADS false
#    define __VOLATILE
# endif

__STL_BEGIN_NAMESPACE

#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
#pragma set woff 1174
#endif

#ifdef __STL_STATIC_TEMPLATE_MEMBER_BUG
#    ifdef __DECLARE_GLOBALS_HERE
void (* __malloc_alloc_oom_handler)() = 0;
#else
extern void (* __malloc_alloc_oom_handler)();
# endif
#endif

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)
{
void *result = malloc(n);
if (0 == result) result = oom_malloc(n);
return result;
}

static void deallocate(void *p, size_t)
{
free(p);
}

static void * reallocate(void *p, size_t , size_t new_sz)
{
void *result = realloc(p, new_sz);
if (0 == result) result = oom_realloc(p, new_sz);
return result;
}

static 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
// 分配内存时,没有内存时的处理

#ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG
template <int inst>
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);
}
}

typedef __malloc_alloc_template<0> malloc_alloc;

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

// Allocator adaptor to check size arguments for debugging.
template <class Alloc>
class debug_alloc
{
private:
enum { extra = 8};    // Size of space used to store size. Note
// that this must be large enough to preserve
// alignment.

public:
static void * allocate(size_t n)
{
char *result = (char *)Alloc::allocate(n + extra);
*(size_t *)result = n;    //前size_t大小用来记录result的大小,实际预分配了extra个字节,用来存储大小,
//但是只用size_t字节,因为不同系统size_t大小不同,8个字节足够满足所有系统了
return result + extra;
}

static void deallocate(void *p, size_t n)
{
char * real_p = (char *)p - extra;
assert(*(size_t *)real_p == n);
Alloc::deallocate(real_p, n + extra);
}

static void * reallocate(void *p, size_t old_sz, size_t new_sz)
{
char * real_p = (char *)p - extra;
assert(*(size_t *)real_p == old_sz);
char * result = (char *)
Alloc::reallocate(real_p, old_sz + extra, new_sz+ extra);
*(size_t *)result = new_sz;
return result + extra;
}

};

#ifdef __USE_MALLOC

typedef malloc_alloc alloc;
typedef malloc_alloc single_client_alloc;

#else

//下面是第二级配置器
//主要是维护一个内存池,用来小于128byte的小型区块内存的分配
//其中,有多个链表,各链表中的node大小从8-128byte,都是8的倍数
//分配时,不是8的倍数,上调至最近的8的倍数,
//然后从相应链表中取下一个对应大小的node分配给请求
#ifdef __SUNPRO_CC
enum {__ALIGN = 8};  //小型区块的上调边界
enum {__MAX_BYTES = 128};   //小型区块的上限
enum {__NFREELISTS = __MAX_BYTES/__ALIGN};
#endif

//第二级配置器
template <bool threads, int inst>
class __default_alloc_template
{
private:
# ifndef __SUNPRO_CC
enum {__ALIGN = 8};  //小型区块的上调边界
enum {__MAX_BYTES = 128};   //小型区块的上限
enum {__NFREELISTS = __MAX_BYTES/__ALIGN};
# endif
//大小上调至8的倍数
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]; //用于存储实际区块的内存地址,由于这是一个union,很好的节约了这个数据的内存
};
private:
# ifdef __SUNPRO_CC
static obj * __VOLATILE free_list[];
# else
static obj * __VOLATILE free_list[__NFREELISTS];
# endif
static size_t FREELIST_INDEX(size_t bytes)
{
return (((bytes) + __ALIGN-1)/__ALIGN - 1);
}

//返回大小为n的对象,并可能加入大小为n的其他区块到free list
static void *refill(size_t n);
//配置一块空间,可容纳nobjs个大小为"size"的区块
//如果配置nobjs个区块有所不便,nobjs可能会降低
static char *chunk_alloc(size_t size, int &nobjs);

//chunk 分配、配置的状态
static char *start_free; //内存池起始位置。只在chunk_alloc()中变化
static char *end_free;   //内存池结束位置。只在chunk_alloc()中变化
static size_t heap_size;
/*
# ifdef __STL_SGI_THREADS
static volatile unsigned long __node_allocator_lock;
static void __lock(volatile unsigned long *);
static inline void __unlock(volatile unsigned long *);
# endif
*/

# ifdef __STL_PTHREADS
static pthread_mutex_t __node_allocator_lock;
# endif

# ifdef __STL_WIN32THREADS
static CRITICAL_SECTION __node_allocator_lock;
static bool __node_allocator_lock_initialized;

public:
__default_alloc_template()
{
//假定第一个构造函数的调用在线程启动起
if (!__node_allocator_lock_initialized)
{
InitializedCriticalSection(&__node_allocator_lock);
__node_allocator_lock_initialized = true;
}
}
private:
# endif

class lock
{
public:
lock() { __NODE_ALLOCATOR_LOCK; }
~lock() { __NODE_ALLOCATOR_UNLOCK; }
};
friend class lock;
public:
//n必须大于0
static void * allocate(size_t n)
{
obj * __VOLATILE * my_free_list;
obj * __RESTRICT result;

//需要分配的大小大于二级配置器的__MAX_BYTES,直接使用第一级配置器
if (n > (size_t) __MAX_BYTES)
{
return(malloc_alloc::allocate(n));
}
my_free_list = free_list + FREELIST_INDEX(n); //找到比需要分配的大小大,且最接近的大小块所在的链表所在free_list数组中的位置

//如果支持线程,定义lock
#        ifndef _NOTHREADS
lock lock_instance;
#        endif
result = *my_free_list;  //取出找的对应链表的指向第一个节点的指针
if (result == 0)    //对应的链表中没有剩余未分配的节点区块
{
void *r = refill(ROUND_UP(n));    //再从内存池中分配一批,需求大小的区块(实际大小是请求大小上调至8的倍数后的数值),
//然后,放入对应链表,待分配给请求
return r;
}
//如果对应大小区块的链表中不为空,还有待分配的区块,取出第一个节点
*my_free_list = result -> free_list_link;
return (result);
};

//p不可以是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);
//需要修改my_free_list,如果支持线程,那么需要加上互斥锁
#            ifndef _NOTHREADS
lock lock_instance;
#            endif

//头插法,插入对应大小的区块链表
q -> free_list_link = *my_free_list;
*my_free_list = q;
//lock是静态对象,到此,将自动析构销毁,在其析构函数中,会释放锁
}

static void *reallocate(void *p, size_t old_sz, size_t new_sz);

};

typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc;
typedef __default_alloc_template<false, 0> single_client_alloc;

// 我们从大的chunks中分配内存,是为了避免使用malloc太频繁了
// 假设size已经适当上调至8的倍数
// 我持有allocation lock
// 注意参数objs 是pass by reference
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 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;
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)
{
//内存池中内还有一些零头,先配给适当的free list
//首先寻找适当的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)
{
//如果heap空间不足,malloc()失败
int i;
obj * __VOLATILE *my_free_list, *p;
//试着检视我们手上的东西。这不会造成伤害。我们不打算尝试配置
//较小的区块,因为那在多线程机器上容易导致灾难
//以下搜索适当的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以释放出未用的区块到内存池
*my_free_list = p -> free_list_link;
start_free = (char *)p;
end_free = start_free + i;
// 此时内存池已经有内存了
// 递归调用自己,为了修正objs
return chunk_alloc(size, nobjs);
//注意,任何残余的零头终将被编入适当的free list中备用

}
}
end_free = 0;  //如果出现意外(山穷水尽,到处都没有内存可用了)
//调用第一级配置器,看看out-of-memory机制能否尽点力
start_free = (char *)malloc_alloc::allocate(bytes_to_get);
//这会导致抛出异常,或内存不足的情况获得改善
}
heap_size += bytes_to_get;
end_free = start_free + bytes_to_get;
//递归调用自己,为了修正objs
return chunk_alloc(size, nobjs);
}
}

// 返回一个大小为n的对象,并且有时候会为适当的free list 增加节点
// 假设n已经适当上调至8的倍数
// 我们持有allocation lock
template <bool threads, int inst>
void* __default_alloc_template<threads, inst>::refill(size_t n)
{
int nobjs = 20;  //默认一次分配20个需求大小的区块
char * chunk = chunk_alloc(n, nobjs); //chunk是分配的空间的开始地址,令其类型为char *,主要是因为一个char的大小正好是一个byte
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 list
result = (obj *)chunk;  //这一块准备返回给客端
// 以下导引free list 指向新配置的空间(取自内存池)

//由于chunk是char*,所以加上n,就表示走过n个char,
//一个char正好是一个byte,所以chunk+n现在指向第二个区块
*my_free_list = next_obj = (obj *)(chunk + n);
for (i = 1; ; ++i)
{
// 从1开始,因为第0个将返回给客端
current_obj = next_obj;
// 每次移动n个char,正好是n个byte,所以正好指向下个区块
next_obj = (obj *)((char *)next_obj + n);
if (nobjs - 1 == i)
{
// 已经遍历完,此时next_obj指向的内存已经超出我们分配的大小了
// 不属于我们的内存
current_obj -> free_list_link = 0;
break;
}
else
{
current_obj -> free_list_link = next_obj;
}
}
return result;
}

template<bool threads, int inst>
void*
__default_alloc_template<threads, inst>::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;

}

#ifdef __STL_PTHREADS
template <bool threads,int inst>
pthread_mutex_t
__default_alloc_template<threads, inst>::__node_allocator_lock
= PTHREAD_MUTEX_INITIALIZER;
#endif

#ifdef __STL_WIN32THREADS
template <bool threads, int inst> CRITICAL_SECTION
__default_alloc_template<threads, inst>::__node_allocator_lock;

template <bool threads, int inst> bool
__default_alloc_template<threads, inst>::__node_allocator_lock_initialized
= false;
#endif

//省略了通用lock的实现(即不使用pthread,也没有win32thread)

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; //设置初始值

//初始化16种大小的区块链表为空
template <bool threads, int inst>
typename __default_alloc_template<threads, inst>::obj * __VOLATILE
__default_alloc_template<threads, inst>::free_list[
# ifdef __SUNPRO_CC
__NFREELISTS
# else
__default_alloc_template<threads, inst>::__NFREELISTS
# endif
] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };

# ifdef __STL_WIN32THREADS
// Create one to get critical section initialized.
// We do this onece per file, but only the first constructor
// does anything.
static alloc __node_allocator_dummy_instance;
# endif

# endif /* ! __USE_MALLOC */

#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
#pragma reset woff 1174
#endif

__STL_END_NAMESPACE

#undef __PRIVATE

#endif /* __SGI_STL_INTERNAL_ALLOC_WJZH_H */

//End


View Code

  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: