【笔记】《STL源码剖析》chapter2 空间配置器
chapter2 空间配置器
2.2 具备次配置力的SGI空间配置器
以下代码都在<stl_alloc.h>
中
SGI_STL使用两级配置器:
-
如果申请内存>128bytes,将使用__malloc_alloc_template
-
如果申请内存<=128bytes, 将使用__default_alloc_template
在__default_alloc_template可以找到:
// breaks if we make these template class members:
enum {__ALIGN = 8}; // 内存调整步长
enum {__MAX_BYTES = 128}; // 阈值
enum {__NFREELISTS = __MAX_BYTES / __ALIGN}; // 空闲表的数量
再查找__MAX_BYTES的调用:
第一级配置器 __malloc_alloc_tempalte 剖析
在__malloc_alloc_template中,STL简单地使用C语言的malloc和free实现内存申请和释放
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 /* n */ ) {
free( p );
}
static void *reallocate( void *p, size_t /* old_sz */, size_t new_sz ) {
void *result = realloc( p, new_sz );
if ( 0 == result )
result = oom_realloc( p, new_sz );
return result;
}
如果申请失败if(result == 0) 就调用oom_alloc再次尝试
template <int inst>
void *__malloc_alloc_template<inst>::oom_malloc( size_t n ) {
void ( * my_malloc_handler )();
void *result;
for ( ;; ) { // 多次尝试,其中会调用my_malloc_handler进行调整,这个函数指针由“用户”设置
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 );
}
}
my_malloc_hadler由"用户"设置,如果没被设置将直接输出错误并退出进程
第二级配置器__default_alloc_template剖析
__dedault_alloc_tempalte使用层次配置,每次申请一大块内存,将其分为8的倍数大小的块,并将相同的块链接成链表,再将这些链表组成一个空闲链表数组free_lists
其中大内存由三个指针进行标记,他们只能由chunk_alloc设置
// Chunk allocation state.
static char *start_free;
static char *end_free;
static size_t heap_size;
free_lists:
union obj {
union obj *free_list_link;
char client_data[1]; /* The client sees this. */
};
static obj *__VOLATILE free_list[__NFREELISTS];
当obj被装入链表时,设置free_list_link指向下一个obj
当用户申请内存时,返回client_data,该指针指向实际区块
实际上这就是链表的头插法,简单高效
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 );
// Acquire the lock here with a constructor call.
// This ensures that it is released in exit or during stack
// unwinding.
# ifndef _NOTHREADS
/*REFERENCED*/
lock lock_instance;
# endif
result = *my_free_list;
if ( result == 0 ) {
void *r = refill( ROUND_UP( n ) );
return r;
}
*my_free_list = result -> free_list_link;
return ( result );
};
/* 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 );
// acquire lock
# ifndef _NOTHREADS
/*REFERENCED*/
lock lock_instance;
# endif /* _NOTHREADS */
q -> free_list_link = *my_free_list;
*my_free_list = q;
// lock is released here
}
空闲链表填充refill:
template <bool threads, int inst>
void *__default_alloc_template<threads, inst>::refill( size_t n ) {
int nobjs = 20; // 缺省填充20个obj --annotated by hsby--
char *chunk = chunk_alloc( n, nobjs ); // 向内存池申请整块内存 --annotated by hsby --
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 );
/* Build free list in chunk */
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 );
}
内存池管理chunk_alloc:
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 ) { // 如果内存池满足需求 --annotated by hsby--
result = start_free;
start_free += total_bytes;
return ( result );
} else if ( bytes_left >= size ) { // 如果内存池有一个以上size大小的块 --annotated by hsby--
nobjs = bytes_left / size;
total_bytes = size * nobjs;
result = start_free;
start_free += total_bytes;
return ( result );
} else { // 否则申请内存,在此之前需要将内存池小块内存分配到合适的空闲链表 --annotated by hsby--
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 ) {
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;
}
start_free = ( char * )malloc( bytes_to_get );
if ( 0 == start_free ) { // 如果申请内存失败,就从比n大的空闲链表中分裂出 --annotated by hsby--
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. //如果还是失败,只能调用一级配置器继续尝试,此方法将可能抛出bad_alloc --annotated by hsby--
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 += bytes_to_get;
end_free = start_free + bytes_to_get;
return ( chunk_alloc( size, nobjs ) );
}
}
在SGI容器中是如何用到内存配置器的,我们可以看看常用容器vector
其中的缺省参数alloc在 <stl_alloc.h>
中默认定义为有锁的二级内存配置器
在vector中还可以找到
可以看出vector使用配置器标准接口simple_alloc<value_type, Alloc>每次处理一个元素的内存
2.3 内存基本处理工具
STL定义了五个全局函数,其中construct()和destroy()在前面讲过,另外3个函数分别是uninitialized_copy()、uninitialized_fill()和uninitialized_fill_n(),分别对应高级别的STL算法函数copy()、fill()、fill_n()
uninitialized_copy()
template <class InputIterator, class ForwardIterator>
inline ForwardIterator __uninitialized_copy_aux( InputIterator first, InputIterator last,
ForwardIterator result,
__true_type ) {
return copy( first, last, result );
}
template <class InputIterator, class ForwardIterator>
ForwardIterator __uninitialized_copy_aux( InputIterator first, InputIterator last,
ForwardIterator result,
__false_type ) {
ForwardIterator cur = result;
__STL_TRY {
for ( ; first != last; ++first, ++cur )
construct( &*cur, *first );
return cur;
}
__STL_UNWIND( destroy( result, cur ) );
}
template <class InputIterator, class ForwardIterator, class T>
inline ForwardIterator __uninitialized_copy( InputIterator first, InputIterator last,
ForwardIterator result, T * ) {
typedef typename __type_traits<T>::is_POD_type is_POD;
return __uninitialized_copy_aux( first, last, result, is_POD() );
}
template <class InputIterator, class ForwardIterator>
inline ForwardIterator uninitialized_copy( InputIterator first, InputIterator last,
ForwardIterator result ) {
return __uninitialized_copy( first, last, result, value_type( result ) );
}