C++ STL源码学习(内存配置篇)

10 篇文章 0 订阅
9 篇文章 0 订阅


<span style="font-size:24px;"><span style="font-size:18px;"># include <stdio.h>
#include <stdlib.h>
#include <new>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

///STL内存分配由两级内存配置器来完成,第一级用来处理大块内存的分配
///因此每次都通过malloc和realloc直接分配,相对简单.第二级用来处理小块内存
///的分配,借用第一级内存配置器分配的大块内存建立内存池,然后再根据需要
///详细的划分.其目的是为了减少内存碎片.STL所有容器默认采用第二级内存配置器

template <int __inst>      ///模板实际没有被用到
class __malloc_alloc_template
{

private:

    static void* _S_oom_malloc(size_t);      ///malloc 失败处理
    static void* _S_oom_realloc(void*, size_t);      ///realloc 失败处理

    ///返回值为void类型的无参函数指针,分配内存请求无法满足时用此函数处理,由客户端代码提供该函数
    static void (* __malloc_alloc_oom_handler)();

public:

    static void* allocate(size_t __n)
    {
        void* __result = malloc(__n);
        if (0 == __result) __result = _S_oom_malloc(__n);   ///malloc失败,调用处理函数
        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 = _S_oom_realloc(__p, __new_sz);  ///realloc失败,调用处理函数
        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

///static函数指针的定义、初始化
///客户端代码未提供内存分配请求失败时的处理函数,则该指针为0
template <int __inst>
void (* __malloc_alloc_template<__inst>::__malloc_alloc_oom_handler)() = 0;

template <int __inst>
void*
__malloc_alloc_template<__inst>::_S_oom_malloc(size_t __n)   ///malloc失败时的处理函数
{
    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);
    }
}

///和_S_oom_malloc基本一样,只是采用了realloc.
template <int __inst>
void* __malloc_alloc_template<__inst>::_S_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;

///为了方便客户使用,对用内存配置器的一层外包装
///_Alloc:实际采用的内存配置器
///_Tp:需要分配的对象类型,依据对象类型和需要处理的对象数目来确定实际需要处理的内存大小
template<class _Tp, class _Alloc>
class simple_alloc
{
public:
    static _Tp* allocate(size_t __n)
    {
        return 0 == __n ? 0 : (_Tp*) _Alloc::allocate(__n * sizeof (_Tp));
    }
    static _Tp* allocate(void)
    {
        return (_Tp*) _Alloc::allocate(sizeof (_Tp));
    }
    static void deallocate(_Tp* __p, size_t __n)
    {
        if (0 != __n) _Alloc::deallocate(__p, __n * sizeof (_Tp));
    }
    static void deallocate(_Tp* __p)
    {
        _Alloc::deallocate(__p, sizeof (_Tp));
    }
};

/// Allocator adaptor to check size arguments for debugging.
/// Reports errors using assert.  Checking can be disabled with
/// NDEBUG, but it's far better to just use the underlying allocator
/// instead when no checking is desired.
///There is some evidence that this can confuse Purify.

///用来测试_Alloc的正确性.每次有内存分配请求时都多分配一块内存
///这块多分配的内存上写入内存大小,当需要reallocate或者回收内存时
///用assert检查客户提供的size和分配时写入的size是否一致,每次realloc
///成功后都将修改其对应的size
template <class _Alloc>
class debug_alloc
{

private:

///多分配8字节以保存分配的内存大小
    enum {_S_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 + (int) _S_extra);   ///多分配_S_extra字节大小内存
        *(size_t*)__result = __n;                                         ///在实际分配的内存块之前写入内存大小
        return __result + (int) _S_extra;                  ///返回给客户端内存大小记录块之后的n字节内存
    }

    static void deallocate(void* __p, size_t __n)
    {
        char* __real_p = (char*)__p - (int) _S_extra;
        assert(*(size_t*)__real_p == __n);               ///检查记录值与客户端提供值是否相同
        _Alloc::deallocate(__real_p, __n + (int) _S_extra);
    }

    static void* reallocate(void* __p, size_t __old_sz, size_t __new_sz)
    {
        char* __real_p = (char*)__p - (int) _S_extra;
        assert(*(size_t*)__real_p == __old_sz);
        char* __result = (char*)
                         _Alloc::reallocate(__real_p, __old_sz + (int) _S_extra,
                                            __new_sz + (int) _S_extra);
        *(size_t*)__result = __new_sz;     ///修改记录值
        return __result + (int) _S_extra;
    }

};


/// Default node allocator.
/// With a reasonable compiler, this should be roughly as fast as the
/// original STL class-specific allocators, but with less fragmentation.
/// Default_alloc_template parameters are experimental and MAY
/// DISAPPEAR in the future.  Clients should just use alloc for now.
///
/// Important implementation properties:
/// 1. If the client request an object of size > _MAX_BYTES, the resulting
///    object will be obtained directly from malloc.
/// 2. In all other cases, we allocate an object of size exactly
///    _S_round_up(requested_size).  Thus the client has enough size
///    information that we can return the object to the proper free list
///    without permanently losing part of the object.
///

/// The first template parameter specifies whether more than one thread
/// may use this allocator.  It is safe to allocate an object from
/// one instance of a default_alloc and deallocate it with another
/// one.  This effectively transfers its ownership to the second one.
/// This may have undesirable effects on reference locality.
/// The second parameter is unreferenced and serves only to allow the
/// creation of multiple default_alloc instances.
/// Node that containers built on different allocator instances have
/// different types, limiting the utility of this approach.

///STL二级内存适配器,如果内存处理请求超过_MAX_BYTES(128)时,直接调用
///第一级适配器直接处理,否则将需要处理的内存块上调至_ALIGN (8)的倍数.
///然后处理.其处理是通过将第一级适配器分配的大块内存划分为_MAX_BYTES/_ALIGN个链表
///,它们的节点大小为_ALIGN,_ALIGN*2,_ALIGN*3,....直到_MAX_BYTES大小的内存
///分别用来处理对应的内存配置

enum {_ALIGN = 8};
enum {_MAX_BYTES = 128};
enum {_NFREELISTS = 16}; /// _MAX_BYTES/_ALIGN

template <bool threads, int inst>
class __default_alloc_template
{

private:
    static size_t
    _S_round_up(size_t __bytes)    ///计算离其最近的_ALIGN的倍数
    {
        return (((__bytes) + (size_t) _ALIGN-1) & ~((size_t) _ALIGN - 1));
    }

private:
///在配置内存时每个小于_MAX_BYTES的内存块都当链表结点使用,并隶属于对应的链表管理,
///提供给客户时作为一般内存块提供.
///相当于为方便内存配置,在数组中建立链表来管理,但客户不需要看到分配时的链表结点和指针
///只需要看到整个结点大小的可用内存块.
    union _Obj
    {
        union _Obj* _M_free_list_link;
        char _M_client_data[1];    ///The client sees this.
    };

private:
    ///_Obj* 类型的数组,该数组存储每个链表的头指针,这每个链表实际上都是一整块内存
    ///否则的话就起不到减少内存碎片的作用了.
    static _Obj* __STL_VOLATILE _S_free_list[];

    ///根据需要处理的内存大小求出其所对应的链表头指针在 _S_free_list数组中的位置
    static  size_t _S_freelist_index(size_t __bytes)
    {
        return (((__bytes) + (size_t)_ALIGN-1)/(size_t)_ALIGN - 1);
    }

    /// Returns an object of size __n, and optionally adds to size __n free list.
///链表为空时填充链表
    static void* _S_refill(size_t __n);
    /// Allocates a chunk for nobjs of _size size.  nobjs may be reduced
    /// if it is inconvenient to allocate the requested number.
    ///从内存池分配内存,不能满足要求时重新分配内存池
    static char* _S_chunk_alloc(size_t __size, int& __nobjs);

    /// Chunk allocation state.
    static char* _S_start_free;      ///内存池起始位置
    static char* _S_end_free;        ///内存池结束位置

    ///内存池分配基数,随着内存池分配次数增多,_S_heap_size也增大,以减少过多的内存池分配
    static size_t _S_heap_size;

public:

    ///__n must be > 0
    static void* allocate(size_t __n)
    {
        void* __ret = 0;

        if (__n > (size_t) _MAX_BYTES)          ///足够大,调用第一级配置器
        {
            __ret = malloc_alloc::allocate(__n);
        }
        else
        {

            ///_S_free_list是_Obj*类型的指针数组名,_S_free_list + n 当然是_Obj**类型了
            ///volatile表示容易变化的,和const相反.
            _Obj*  volatile* __my_free_list
                = _S_free_list + _S_freelist_index(__n);    ///找到对应链表头指针在_S_free_list中的位置


            _Obj* __RESTRICT __result = *__my_free_list;     ///result为对应链表的头指针

            if (__result == 0)              ///对应链表为空,请求配置该链表
                __ret = _S_refill(_S_round_up(__n));
            else
            {
                ///将该链表的头一个结点返回给客户端,链表头指向下一个结点
                *__my_free_list = __result -> _M_free_list_link;
                __ret = __result;
            }
        }

        return __ret;
    };

    /// __p may not be 0
    static void deallocate(void* __p, size_t __n)
    {
        if (__n > (size_t) _MAX_BYTES)
            malloc_alloc::deallocate(__p, __n);
        else
        {
            _Obj* __STL_VOLATILE*  __my_free_list
                = _S_free_list + _S_freelist_index(__n);
            _Obj* __q = (_Obj*)__p;

            ///将客户返还的内存插入到对应链表的头部
            __q -> _M_free_list_link = *__my_free_list;
            *__my_free_list = __q;
        }
    }

    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;

template <bool __threads, int __inst>
inline bool operator==(const __default_alloc_template<__threads, __inst>&,
                       const __default_alloc_template<__threads, __inst>&)
{
    return true;
}


/// We allocate memory in large chunks in order to avoid fragmenting
///the malloc heap too much.
/// We assume that size is properly aligned.


template <bool __threads, int __inst>
char*
__default_alloc_template<__threads, __inst>::_S_chunk_alloc(size_t __size,
        int& __nobjs)  ///此处时用的是引用,方便将__nobjs修改为实际分配的数量
{
    char* __result;
    size_t __total_bytes = __size * __nobjs;
    size_t __bytes_left = _S_end_free - _S_start_free;   ///计算内存池剩余水量

    if (__bytes_left >= __total_bytes)             ///完全满足要求
    {
        __result = _S_start_free;
        _S_start_free += __total_bytes;    ///从内存池中得到内存,调整内存池水位
        return(__result);

    }
    else if (__bytes_left >= __size)            ///不完全满足要求,但可以部分满足
    {
        __nobjs = (int)(__bytes_left/__size);     ///计算最多可分配的数量
        __total_bytes = __size * __nobjs;
        __result = _S_start_free;
        _S_start_free += __total_bytes;
        return(__result);

    }
    else                                                 ///内存池连一个__size大小的内存都不到,重新分配内存池
    {

        ///计算新的内存池大小
        size_t __bytes_to_get =
            2 * __total_bytes + _S_round_up(_S_heap_size >> 4);  ///重新分配_S_heap_size变为原来的16倍

        /// Try to make use of the left-over piece.
        if (__bytes_left > 0)
        {
            ///将原来内存池剩余的内存块插入到对应的链表
            _Obj* __STL_VOLATILE* __my_free_list =
                _S_free_list + _S_freelist_index(__bytes_left);

            ((_Obj*)_S_start_free) -> _M_free_list_link = *__my_free_list;
            *__my_free_list = (_Obj*)_S_start_free;
        }

        ///使用malloc重新分配,不去使用一级适配器是因为一旦分配失败,我们还有办法处理,
        ///而不是直接调用客户提供的内存分配失败处理函数或是简单的抛出异常
        _S_start_free = (char*)malloc(__bytes_to_get);

        if (0 == _S_start_free)       ///使用malloc重新分配失败
        {
            ///从链表中寻找可用的内存块

            size_t __i;
            _Obj* __STL_VOLATILE* __my_free_list;
            _Obj* __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 <= (size_t) _MAX_BYTES;
                    __i += (size_t) _ALIGN)
            {
                __my_free_list = _S_free_list + _S_freelist_index(__i);
                __p = *__my_free_list;

                if (0 != __p)
                {
                    ///找到一个可用的内存块,将其从所属链表中拔出,作为内存池
                    *__my_free_list = __p -> _M_free_list_link;
                    _S_start_free = (char*)__p;
                    _S_end_free = _S_start_free + __i;

                    ///此时内存池大小至少已可部分满足要求,递归调用_S_chunk_alloc再次处理
                    return(_S_chunk_alloc(__size, __nobjs));
                    /// Any leftover piece will eventually make it to the
                    /// right free list.
                }
            }

            ///程序运行到此处,说明malloc分配失败,链表中也无可用内存块
            _S_end_free = 0;	/// In case of exception.
            ///调用一级配置器是为了调用客户提供的内存分配处理失败函数,或是抛出异常
            _S_start_free = (char*)malloc_alloc::allocate(__bytes_to_get);
            ///This should either throw an
            /// exception or remedy the situation.  Thus we assume it
            /// succeeded.
        }

        ///程序运行到此处,说明客户提供的函数有效地处理了内存不足问题,分配到了合适大小的内存
        ///调整内存池水位,递归调用_S_chunk_alloc继续处理
        _S_heap_size += __bytes_to_get;    ///_S_heap_size增加__bytes_to_get,在避免过多内
        ///存池分配和内存不足件取得平衡
        _S_end_free = _S_start_free + __bytes_to_get;
        return(_S_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.
template <bool __threads, int __inst>
void*
__default_alloc_template<__threads, __inst>::_S_refill(size_t __n)
{
    int __nobjs = 20;
    char* __chunk = _S_chunk_alloc(__n, __nobjs);    ///从内存池获得一定数量链表结点大小的内存块,最多为20个
    ///objs被修改为实际分配到的个数
    _Obj* __STL_VOLATILE* __my_free_list;
    _Obj* __result;
    _Obj* __current_obj;
    _Obj* __next_obj;
    int __i;

    if (1 == __nobjs) return(__chunk);            ///只分配到一个,直接返回给客户端就行

    ///分配到多个,则除了要返回个客户端一个之外,还要将剩下的内存以链表结点的形式插入到对应链表
    __my_free_list = _S_free_list + _S_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 -> _M_free_list_link = 0;
            break;
        }
        else
        {
            __current_obj -> _M_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 (_S_round_up(__old_sz) == _S_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);
}

///初始状态,内存池为空,所有链表为空,内存池分配基数为0
template <bool __threads, int __inst>
char* __default_alloc_template<__threads, __inst>::_S_start_free = 0;

template <bool __threads, int __inst>
char* __default_alloc_template<__threads, __inst>::_S_end_free = 0;

template <bool __threads, int __inst>
size_t __default_alloc_template<__threads, __inst>::_S_heap_size = 0;

template <bool __threads, int __inst>
typename __default_alloc_template<__threads, __inst>::_Obj* __STL_VOLATILE
__default_alloc_template<__threads, __inst> ::_S_free_list[_NFREELISTS]
    = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };

typedef __default_alloc_template<false,0> Stl_Default_Alloc;
</span></span>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值