SGI STL内存管理

在SGI STL版本的内存管理中,使用这样一种方式来分配内存:内存分配+对象初始化。首先是分配内存,其次是根据对象的类型(是否为POD【Plain of Data】)来使用最有效的方式来初始化对象。回收内存也是用同样的方式:析构对象+回收内存,根据对象是否为POD类型,确定最有效的析构方式。

SGI STL使用双层级配置器,第一级配置器直接使用malloc()和free(),第二级根据如下策略:当配置区块>128 Bytes时,视之为“足够大”,调用一级配置器,否则视之为过小,调用二级配置器。

一级配置器:__malloc_alloc_template

template <int __inst>        //非型别参数,没排上用处
class __malloc_alloc_template
{
    private:
        static void* _S_oom_malloc(size_t);        //用来处理内存不足的情况,out of memory
        static void* _S_oom_realloc(void*, size_t);
        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);
            return __result;
        }

        static void deallocate(void* __p, size_t )        //释放内存
        {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);
            return __result;
        }

        static void (* __set_malloc_handler(void (*__f)()))()        //指定自己的oom handler
        {
            void (* __old)() = __malloc_alloc_oom_handler;
            __malloc_alloc_oom_handler = __f;
            return(__old);
        }
};

template <int __inst>
void (* __malloc_alloc_template<__inst>::__malloc_alloc_oom_handler)() = 0;        //默认没有oom处理

template <int __inst>
void* __malloc_alloc_template<__inst>::_S_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>::_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);
    }
}

二级配置器:__default_alloc_template

template <bool threads, int inst>
class __default_alloc_template
{
    private:
        enum {_ALIGN = 8};        //每个小内存块的大小相差为8
        enum {_MAX_BYTES = 128};        //最大的小内存的大小为128bytes
        enum {_NFREELISTS = 16};         //小内存块的类型

        static size_t _S_round_up(size_t __bytes)        //将需要的小内存块大小上调为8的倍数
        { return (((__bytes) + (size_t) _ALIGN-1) & ~((size_t) _ALIGN - 1)); }    //(x+7)& ~7

        union _Obj         //在区块中保存的对象类型,为了节省内存,使用这种方式
        {
            union _Obj* _M_free_list_link;
            char _M_client_data[1];
        };

    private:
        static _Obj* __STL_VOLATILE _S_free_list[_NFREELISTS];         //存储对象链表

        static  size_t _S_freelist_index(size_t __bytes)     //根据需要分配的对象,获取区块索引
        {return (((__bytes) + (size_t)_ALIGN-1)/(size_t)_ALIGN - 1);}

        static void* _S_refill(size_t __n);
        static char* _S_chunk_alloc(size_t __size, int& __nobjs);

        static char* _S_start_free;        //起始,结束
        static char* _S_end_free;
        static size_t _S_heap_size;

    public:
        static void* allocate(size_t __n)        //获取内存块
        {
            void* __ret = 0;

            if (__n > (size_t) _MAX_BYTES)         //如果需要的内存块大小>128,调用一级配置器进行分配
                return __ret = malloc_alloc::allocate(__n);

            _Obj* __STL_VOLATILE* __my_free_list = _S_free_list + _S_freelist_index(__n);    //获取需要的free_list
            _Obj* __RESTRICT __result = *__my_free_list;        //从链表头部获取一块小内存

            if (__result == 0)    //如果没有找到可用的free_list,准备重新填充free list
            {
                __ret = _S_refill(_S_round_up(__n));
                return __ret;
            } 

            *__my_free_list = __result -> _M_free_list_link;    //调整free_list的头部指向下一个

            return __result;
        };

        static void deallocate(void* __p, size_t __n)    //释放一块小的区块至free_list
        {
            if (__n > (size_t) _MAX_BYTES)    //如果想要归还的区块>128,调用一级配置器
                malloc_alloc::deallocate(__p, __n);
            else
            {
                _Obj* __STL_VOLATILE*  __my_free_list = _S_free_list + _S_freelist_index(__n);        //找到合适的free_list
                _Obj* __q = (_Obj*)__p;        //临时存储即将归还的小区快

                __q -> _M_free_list_link = *__my_free_list;    //将这个归还的小区块置为free_list的头部
                *__my_free_list = __q;        //调整区块列表的头部指针
            }
        }

        static void* reallocate(void* __p, size_t __old_sz, size_t __new_sz);
} ;

template <bool __threads, int __inst>
void* __default_alloc_template<__threads, __inst>::_S_refill(size_t __n)        //为内存不足的free_list重新填充空间
{
    int __nobjs = 20;
    char* __chunk = _S_chunk_alloc(__n, __nobjs);        //尝试调用chunk_alloc获取20个新的区块作为free_list的新节点
    _Obj* __STL_VOLATILE* __my_free_list;
    _Obj* __result;
    _Obj* __current_obj;
    _Obj* __next_obj;
    int __i;

    if (1 == __nobjs)     //如果获取的区块数量为1,这块可以直接返回,等下次内存不足时在做处理
        return(__chunk);
    __my_free_list = _S_free_list + _S_freelist_index(__n);    //在链表中获取合适的free_list

    __result = (_Obj*)__chunk;        //从刚刚获取的20个区块中,先拿走一个,作为返回的结果
    *__my_free_list = __next_obj = (_Obj*)(__chunk + __n);        //将第二个区块作为头部,然后通过循环,将这些区块添加到free_list中
    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>
char* __default_alloc_template<__threads, __inst>::_S_chunk_alloc(size_t __size, int& __nobjs)        //从内存池中获取足够的内存给free_list使用
{
    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_t __bytes_to_get = 2 * __total_bytes + _S_round_up(_S_heap_size >> 4);        //如果需要重新分配内存,这是需要重新分配内存的数量
        if (__bytes_left > 0)         //如果内存池中还有零头,将零头加入free_list中
        {
            _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;
        }
        _S_start_free = (char*)malloc(__bytes_to_get);    //配置heap空间,用来补充内存池
        if (0 == _S_start_free)          //如果从堆中获取内存失败,我们将采用如下策略:检验我们手上空闲的,较大的内存块,将这个较大的内存块归还到堆中,然后就可以重新进行配置了
        {
            size_t __i;
            _Obj* __STL_VOLATILE* __my_free_list;
        _Obj* __p;

            for (__i = __size;__i <= (size_t) _MAX_BYTES;__i += (size_t) _ALIGN)
            {
                __my_free_list = _S_free_list + _S_freelist_index(__i);    //获取当前的free_list
                __p = *__my_free_list;        //如果这个free_list的head存在
                if (0 != __p) {
                    *__my_free_list = __p -> _M_free_list_link;
                    _S_start_free = (char*)__p;        //将这个头部的free_list归还到堆中去,调整堆指针的位置
                    _S_end_free = _S_start_free + __i;
                    return(_S_chunk_alloc(__size, __nobjs));        //不用担心有剩余,任何残余的零头都会被编入合适的free_list中去
                }
            }

            _S_end_free = 0;    // 一般情况下,不会执行到这一块。如果出现意外,会调用一级配置器的异常处理机制获取内存
            _S_start_free = (char*)malloc_alloc::allocate(__bytes_to_get);
        }
        _S_heap_size += __bytes_to_get;        //一般不会执行到这一块
        _S_end_free = _S_start_free + __bytes_to_get;
        return(_S_chunk_alloc(__size, __nobjs));
    }
}

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[
# if defined(__SUNPRO_CC) || defined(__GNUC__) || defined(__HP_aCC)
    _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, };

标准配置器:simple_alloc

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

基本的内存配置工具

其中最影响效率的因素在于判断是Value否为POD类型,根据是否为POD类型做出不同的处理。

uninitialized_copy

template <class _InputIter, class _ForwardIter>
inline _ForwardIter uninitialized_copy(_InputIter __first, _InputIter __last, _ForwardIter __result)
{
    return __uninitialized_copy(__first, __last, __result, __VALUE_TYPE(__result));        //萃取出__result的类型,判断是否为POD类型
}

inline char* uninitialized_copy(const char* __first, const char* __last, char* __result)        //随之后是两个特化的版本
{
    memmove(__result, __first, __last - __first);
    return __result + (__last - __first);
}

inline wchar_t* uninitialized_copy(const wchar_t* __first, const wchar_t* __last, wchar_t* __result)
{
    memmove(__result, __first, sizeof(wchar_t) * (__last - __first));
    return __result + (__last - __first);
}

template <class _InputIter, class _ForwardIter, class _Tp>
inline _ForwardIter __uninitialized_copy(_InputIter __first, _InputIter __last,_ForwardIter __result, _Tp*)
{
    typedef typename __type_traits<_Tp>::is_POD_type _Is_POD;        //萃取_Tp的类型
    return __uninitialized_copy_aux(__first, __last, __result, _Is_POD());        //利用重载的方式在编译期选择处理
}

template <class _InputIter, class _ForwardIter>
inline _ForwardIter __uninitialized_copy_aux(_InputIter __first, _InputIter __last,_ForwardIter __result, __true_type)
{
    return copy(__first, __last, __result);        //如果是POD类型,直接进行memcpy进行处理就行
}

template <class _InputIter, class _ForwardIter>
_ForwardIter __uninitialized_copy_aux(_InputIter __first, _InputIter __last,_ForwardIter __result,__false_type)
{
    _ForwardIter __cur = __result;        //如果非POD类型,逐个在_result类型上进行construct
    __STL_TRY
    {
        for ( ; __first != __last; ++__first, ++__cur)
            _Construct(&*__cur, *__first);
        return __cur;
    }
    __STL_UNWIND(_Destroy(__result, __cur));
}

uninitialized_copy_n

template <class _InputIter, class _Size, class _ForwardIter>
inline pair<_InputIter, _ForwardIter> uninitialized_copy_n(_InputIter __first, _Size __count,_ForwardIter __result)
{
    return __uninitialized_copy_n(__first, __count, __result,__ITERATOR_CATEGORY(__first));
}

template <class _InputIter, class _Size, class _ForwardIter>
inline pair<_InputIter, _ForwardIter> __uninitialized_copy_n(_InputIter __first, _Size __count,_ForwardIter __result)
{
    return __uninitialized_copy_n(__first, __count, __result,__ITERATOR_CATEGORY(__first));
}

template <class _RandomAccessIter, class _Size, class _ForwardIter>
inline pair<_RandomAccessIter, _ForwardIter> __uninitialized_copy_n(_RandomAccessIter __first, _Size __count,_ForwardIter __result,random_access_iterator_tag)
{
     _RandomAccessIter __last = __first + __count;
    return pair<_RandomAccessIter, _ForwardIter>(__last,uninitialized_copy(__first, __last, __result));
}

template <class _InputIter, class _Size, class _ForwardIter>
pair<_InputIter, _ForwardIter> __uninitialized_copy_n(_InputIter __first, _Size __count,_ForwardIter __result,input_iterator_tag)
{
    _ForwardIter __cur = __result;
    __STL_TRY
    {
        for ( ; __count > 0 ; --__count, ++__first, ++__cur)
            _Construct(&*__cur, *__first);
        return pair<_InputIter, _ForwardIter>(__first, __cur);
    }
    __STL_UNWIND(_Destroy(__result, __cur));
}

uninitialized_fill

template <class _ForwardIter, class _Tp>
inline void uninitialized_fill(_ForwardIter __first,_ForwardIter __last, const _Tp& __x)
{
    __uninitialized_fill(__first, __last, __x, __VALUE_TYPE(__first));
}

template <class _ForwardIter, class _Tp, class _Tp1>
inline void __uninitialized_fill(_ForwardIter __first,_ForwardIter __last, const _Tp& __x, _Tp1*)
{
    typedef typename __type_traits<_Tp1>::is_POD_type _Is_POD;
    __uninitialized_fill_aux(__first, __last, __x, _Is_POD());
}

template <class _ForwardIter, class _Tp>
void __uninitialized_fill_aux(_ForwardIter __first, _ForwardIter __last,const _Tp& __x, __false_type)
{
    _ForwardIter __cur = __first;
    __STL_TRY
    {
        for ( ; __cur != __last; ++__cur)
            _Construct(&*__cur, __x);
    }
    __STL_UNWIND(_Destroy(__first, __cur));
}

template <class _ForwardIter, class _Tp>
inline void __uninitialized_fill_aux(_ForwardIter __first, _ForwardIter __last,const _Tp& __x, __true_type)
{
    fill(__first, __last, __x);
}

uninitialized_fill_n

template <class _ForwardIter, class _Size, class _Tp>
inline _ForwardIter uninitialized_fill_n(_ForwardIter __first, _Size __n, const _Tp& __x)
{
    return __uninitialized_fill_n(__first, __n, __x, __VALUE_TYPE(__first));
}

template <class _ForwardIter, class _Size, class _Tp, class _Tp1>
inline _ForwardIter __uninitialized_fill_n(_ForwardIter __first, _Size __n, const _Tp& __x, _Tp1*)
{
    typedef typename __type_traits<_Tp1>::is_POD_type _Is_POD;
    return __uninitialized_fill_n_aux(__first, __n, __x, _Is_POD());
}

template <class _ForwardIter, class _Size, class _Tp>
_ForwardIter __uninitialized_fill_n_aux(_ForwardIter __first, _Size __n,const _Tp& __x, __false_type)
{
    _ForwardIter __cur = __first;
    __STL_TRY
    {
        for ( ; __n > 0; --__n, ++__cur)
            _Construct(&*__cur, __x);
        return __cur;
    }
    __STL_UNWIND(_Destroy(__first, __cur));
}

template <class _ForwardIter, class _Size, class _Tp>
inline _ForwardIter __uninitialized_fill_n_aux(_ForwardIter __first, _Size __n,const _Tp& __x, __true_type)
{
    return fill_n(__first, __n, __x);
}

原文地址:https://www.cnblogs.com/ukernel/p/9191159.html

时间: 2024-10-08 18:16:42

SGI STL内存管理的相关文章

STL内存管理

过年在家无事看了<STL源码剖析>,开学了将看过的东西总结一下,以防忘记. 先从STL的内存管理开始总结.掌管STL内存控制的是一个叫空间适配器(alloc)的东西.STL有两个空间适配器,SGI标准空间适配器(allocate)和SGI特殊的空间适配器(alloc),前者只是对c++的new和delete进行了简单的封装.下面主要介绍的是alloc空间适配器. 一般而言,c++的内存配置和释放操作是这样的: class Foo {...} Foo* pf = new Foo: delete

图解STL内存管理的两种边界情况(STL源码剖析补充)

图解STL内存管理的两种边界情况(STL源码剖析补充) 第一种情况就是内存池剩余的小字节空间怎么处理,会不会有内存泄露,答案肯定是不会,但是这个过程是怎么处理的,以下的代码已经简化处理,直接放到VS2010里就可以运行 #include<stdio.h> #include<stdlib.h> static const size_t __ALIGN=8; static const size_t __MAX_BYTES=128; static const size_t __NFREEL

SGI STL内存配置器存在内存泄漏吗?

阅读了SGI的源码后对STL很是膜拜,很高质量的源码,从中学到了很多.温故而知新!下文中所有STL如无特殊说明均指SGI版本实现. STL 内存配置器 STL对内存管理最核心部分我觉得是其将C++对象创建过程分解为构造.析构和内存分配.释放两类操作分离开来!摆脱了对频繁调用new或malloc函数想操作系统申请空间而造成的低效.其中析构操作时对具有non-trival.trival 析构函数的class区别对待也提高了效率.SGI 的两级配置器结构属于锦上添花. STL内存配置器有没有内存泄漏?

SGI STL内存配置器(一):内存泄漏?

阅读了Alexander大神的SGI STL源码,膜拜,很高质量的源码,获益匪浅.温故而知新!下文中所有STL如无特殊说明均指SGI版本实现. STL 内存配置器 STL对内存管理最核心部分我觉得是它将C++对象创建过程分解为构造.析构和内存分配.释放!摆脱了由于频繁调用new或malloc函数向操作系统申请空间而造成的低效.其中析构操作时对具有non-trival.trival 析构函数的class区别对待也提高了效率.至于SGI的两级配置器结构则属于锦上添花的类型. STL两级结构的内存配置

STL内存管理器的分配策略

STL提供了很多泛型容器,如vector,list和map.程序员在使用这些容器时只需关心何时往容器内塞对象,而不用关心如何管理内存,需要用多少内存,这些STL容器极大地方便了C++程序的编写.例如可以通过以下语句创建一个vector,它实际上是一个按需增长的动态数组,其每个元素的类型为int整型: stl::vector<int> array; 拥有这样一个动态数组后,用户只需要调用push_back方法往里面添加对象,而不需要考虑需要多少内存: array.push_back(10); a

stl内存管理allocator(转)

1. STL容器简介 STL提供了很多泛型容器,如vector,list和map.程序员在使用这些容器时只需关心何时往容器内塞对象,而不用关心如何管理内存,需要用多少内存,这些STL容器极大地方便了C++程序的编写.例如可以通过以下语句创建一个vector,它实际上是一个按需增长的动态数组,其每个元素的类型为int整型: stl::vector<int> array; 拥有这样一个动态数组后,用户只需要调用push_back方法往里面添加对象,而不需要考虑需要多少内存: array.push_

SGI的内存管理

1. SGI中的内存管理 在SGI中, 其内存分配把这两步独立出了两个函数:allocate 申请内存,construct 调用构造函数,分别在 2.构造和析构的工具 在stl_construct.h中 //第一个版本destory接受一个指针,然后调用析构函数 template <class T> inline void destroy(T* pointer) { pointer->~T(); } //构造函数 template <class T1, class T2> i

c++ stl 内存配置器

在STL中,Memory Allocator 处于最底层的位置,为一切的 Container 提供存储服务,是一切其他组件的基石.对于一般使用 STL 的用户而言,Allocator 是不可见的,如果需要对 STL 进行扩展,如编写自定义的容器,就需要调用 Allocator 的内存分配函数进行空间配置. 在C++中,一个对象的内存配置和释放一般都包含两个步骤,对于内存的配置,首先是调用operator new来配置内存,然后调用对象的类的构造函数进行初始化:而对于内存释放,首先是调用析构函数,

STL之内存管理

STL以泛型思维为基础,提供了6大组件:容器(containers).算法(algorithms).迭代器(iterators).仿函数(functors).适配器(adapters).分配器(allocators). 容器: vector.list.deque.set.map等,用来存放数据.从实现的角度来看,容器就是一种class template. 算法: sort.find.copy等,从实现的角度来看,算法就是一种function template. 迭代器: 从实现的角度来看,迭代器