【笔记】《STL源码剖析》chapter2 空间配置器

chapter2 空间配置器

2.2 具备次配置力的SGI空间配置器

以下代码都在<stl_alloc.h>

SGI_STL使用两级配置器:

  • 如果申请内存>128bytes,将使用__malloc_alloc_template

  • 如果申请内存<=128bytes, 将使用__default_alloc_template

在__default_alloc_template可以找到:

image-20210317004144865

// breaks if we make these template class members:
enum {__ALIGN = 8};			// 内存调整步长
enum {__MAX_BYTES = 128};	// 阈值
enum {__NFREELISTS = __MAX_BYTES / __ALIGN}; // 空闲表的数量

再查找__MAX_BYTES的调用:

image-20210317002946242

第一级配置器 __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

image-20210317094308578

其中的缺省参数alloc在 <stl_alloc.h>中默认定义为有锁的二级内存配置器

image-20210317094528383

在vector中还可以找到

image-20210317094659519

可以看出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 ) );
}
posted @ 2021-04-24 11:02  汉森伯逸  阅读(55)  评论(0编辑  收藏  举报