SGI空间分配器之第二级配置器剖析

template<bool threads,int inst>
class __default_alloc_template
{
private:
    enum {__ALIGN=8};
    enum {__MAX_BYTES=128;};
    enum {__NFREELISTS=__MAX_BYTES/__ALIGN};

    /*struct obj
    {
        struct obj* free_list_link;
    };*/
    //结构体和下面,在此处中是效果一致的。本模板采取此数据结构
    //相对好处,不是很明白
 union obj
    {
        union obj* free_list_link;
        char client_data[1];
    };

    //函数FREELIST_INDEX表示返回free_list[16]一个索引值
    static size_t FREELIST_INDEX(size_t n)
    {
        return (((n)+__ALIGN-1)/__ALIGN-1);//返回对应的0-15的值。
    }
    //函数ROUND_UP出现,是为了对于空间大小是8,16,。。。128固定值,让n量化为其中某个值。
    size_t ROUND_UP(size_t n)
    {
        //等效于return(((n-1)/__ALIGN+1)*__ALIGN);
        return ((n+__ALIGN-1)&~(__ALIGN-1));
    }

    //函数chunk_alloc用于申请空间,想内存池
    char* chunk_alloc(size_t n,size_t nobjs)
    {
        char * result;
        size_t total_bytes=n*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>=n)
        {//满足一个以上
            result=start_free;
            nobjs=bytes_left/n;//修正个数值
            total_bytes=nobjs*n;
            start_free+=total_bytes;
            return result;
        }
        else
        {//一个都满足不了了
            if(bytes_left>0)
            {//有内存;注意由于分配出来的都是8的倍数,则此时内存残余也是8的倍数
                //收集残余内存
                obj* volatile * my_free_list=free_list+FREELIST_INDEX(bytes_left);//适合残余bytes_left的空间收集
                ((obj*)start_free)->free_list_link=*my_free_list;
                 *my_free_list=(obj*)start_free;
            }
            //收集完本池中的残余空间后,则重新申请空间作为内存池。
            //针对要求空间total_bytes,进行分配预留更多的,且考虑附加量,与已经申请了多少堆有关。
            size_t bytes_to_get=2*total_bytes+ROUND_UP(heap_size>>4);
            start_free=(char *)malloc(bytes_to_get);
            if(start_free==0)
            {//申请内存空间失败
                //首先检测比分配空间更大的空间表是否有空闲内存。

                obj *volatile*my_free_list,*p;//注意此处*p是obj *p

                for(i=n;i<=__MAX_BYTES;i+=__ALIGN)
                {
                    my_free_list=free_list+FREELIST_INDEX(i);
                    p=*my_free_list;
                    if(0!=p)
                    {//表示i的空间大小有空闲,则将其空间从中删除,作为内存池
                        *my_free_list=*my_free_list->free_list_link;
                        start_free=(char*)p;
                        end_free=(char*)p+i;
                        chunk_alloc(n,nobjs);//肯定是大于所需空间大小的。
                    }
                }
                //所有的空间链表都没有空闲的了。
                end_free=0;
                //二级内存分配方案已经解决不了了,只能扔给第一级分配器来处理。(以为着不需要内存池了)
                start_free=(char*)malloc_alloc::allocate(bytes_to_get);
                //如果成功返回,那么会继续给二级空间使用。如果不行没有成功,则会扔出错误结束过程。
            }
            heap_size+=bytes_to_get;//表示申请了多少空间
            end_free=start_free+bytes_to_get;
            return chunk_alloc(n,nobjs);//已经分配到空间了,内存池重新注满水了,下一步就是类似前面的返回所需空间了
        }

    }
    //函数refill重新分配固定空间个数
    void* refill(size_t n)
    {
        //默认大小空间数是20;
        int nobjs=20;
        //向内存池中申请空间,以提供给客端空间申请要求
        char *chunk=chunk_alloc(n,nobjs);//nobjs表示希望的个数,如果实际内存池不够,则会修改该值。
        obj * result,*current_obj,*next_obj;

        obj* volatile *my_free_list;

        if(nobjs==1) return(chunk);//此处注意,它表示申请不到20个空间,只能是一个,char*转为void*。

        my_free_list=free_list+FREELIST_INDEX(n);//由于不止一个,则就需要修改空间链表指针。

        result=(obj*)chunk;//char*转为obj*
        next_obj=(obj*)(chunk+n);//char*转为obj*
        *my_free_list=next_obj;//表示所指空间链表首地址
        for(i=1;;i++)
        {
            current_obj=next_obj;
            next_obj=(obj*)((char*)next_obj+n);//obj* 转为char*;char*转为obj*
            if(nobjs-1==i)//表示分配了nobjs个但是有个被用了,故而就只有这么多个
            {
                current_obj->free_list_link=0;//最后一个设置为0;则当它被用了后,则原表中的指针只能值为0;
                break;
            }
            else
                current_obj->free_list_link=next_obj;
        }
        return result;    //obj*转为void*
    }    

    static obj* volatile free_list[__NFREELISTS];
    static char* end_free;
    static char* start_free;
    static size_t heap_size;
public:
    static void *allocate(size_t n)
    {
    //指针值,该指针指向一个obj*值,且是不可优化的,也就是free_list[16]值不可优化。
        obj * volatile *my_free_list;
    //分配空间结果位置---返回时其实质也就转为了void*指针类型了。被用时又转为相应的类型
        obj * result;

        if(n>(size_t)__MAX_BYTES)
            return (malloc_alloc::allocate(n));

        my_free_list=free_list+FREELIST_INDEX;//根据待分配空间大小给出指针所指free_list的值
        //free_list数组所指是空间链表,链表如果没有空间则指向的是0值,否则是空间首地址。
        result=*my_free_list;//所指空间处
        if(0==result)
        {
            //需要申请空间配置扩展该固定字段空间。
            void *p=refill(ROUND_UP(n));//表示分配后空间,返回一个被用,ROUND_UP是表示n上调其固定值。
            return p;
        }

        //如果成功被分配出去了,则下面就得调整本链表空间。
        *my_free_list=result->free_list_link;//只需要修改所指所指首地址,没有空间则指为0

        return result;//obj*转为void*
    }
    static void deallocate(void *p ,size_t n)
    {
        if(n>__MAX_BYTES)
            return (malloc_alloc::deallocate(p,n));

        obj* volatile * my_free_list=free_list+FREELIST_INDEX;
        ((obj*)p)->free_list_link=*my_free_list;
        *my_free_list=(obj*)p;

    }
    static void *reallocate(void *p ,size_t old_sz,size_t new_sz)
    {
        void * result;
        size_t copy_sz;

        //对于老的内存,新的内存都大于128则交给一级处理
        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);//函数用于复制在#include <string.h>;C语言库中

        //复制完后,进行收回原来的内存空间。
        deallocate(p, old_sz);
        return(result);
    }
};
时间: 2024-08-29 03:15:45

SGI空间分配器之第二级配置器剖析的相关文章

SGI空间分配器之第一级配置器剖析

/* 用途:用于分配内存空间的模板,作为第一级分配: 模板形参:无意义,模板内没有使用 接口函数:allocate函数用于分配空间 reallocate函数用于指定地址重新分配空间 deallocate函数用于释放空间 set_malloc_hander函数用于获取“内存不足处理例程”的函数 时间:2014-12-4 */ template <int inst> class __malloc_alloc_template { private: typedef void (*Func)();//

第二级配置器 _ _default_alloc_template 剖析

第二级配置器  _default_alloc_template 版权声明:本文为博主原创文章,未经博主允许不得转载.

STL初探——第二级配置器 __default_alloc_template的学习心得

SGI STL 第二级配置器使用的是memory pool,即内存池,相比较于第一级空间配置器,第二级空间配置器多了许多限制,主要是为了防止申请小额区块过多而造成内存碎片.当然小额区块在配置时实际上是对空间配置器效率的一种伤害.另外,索求任何一块内存,都得需要一些额外内存来进行标记,虽然这些标记占内存很小很小,但蚂蚁多咬死象,小区块多了,这些小标记还是挺浪费内存的,但这也无法避免,毕竟系统需要靠这些小标记管理内存. SGI 第二级配置器的做法是,如果区块足够大,超过128bytes时,就移交第一

STL源码分析--空间配置器 第一级配置器

一.SGI STL配置器简介 SGI STL的配置器与众不同,它与标准规范不同.如果要在程序中明确使用SGI配置器,那么应该这样写: [cpp] view plaincopyprint? vector<int,std::alloc> iv; 他的名字是alloc,而且不接受任何参数.标准配置器的名字是allocator,而且可以接受参数. SGI STL的每一个容器都已经指定了缺省配置器:alloc.我们很少需要自己去指定空间配置器.比如vector容器的声明: [cpp] view plai

【STL】空间配置器剖析(二)

上篇文章点击打开链接主要对于对象的构造含和析构进行了主要说明,这篇文章将对对象构造前的内存配置和对象析构后的空间释放进行深入探索. 好的,话不多说马上进入是正文: 对对象构造前的内存配置和对象析构后的空间释放,由<stl_alloc.h>负责,SGI对此的设计哲学如下: 向system heap要求空间. 考虑多线程的状态 考虑内存不足的应变措施 考虑过多的"小型区块"可能造成的内存碎片问题 C++的内存配置的基本操作是:operator new(),内存释放的基本操作是o

STL-空间配置器剖析

网上有很多对于STL空间配置器源码的剖析,之所以这么多人去剖析空间配置器,我觉得是真的设计的太好,而且剖析空间配置器的架构的设计对于C++学者来说是一个不错的提高能力的项目,所以加入到这个解剖大军中来. 参照了侯捷的<STL源码剖析>,原本直接看源码不懂得东西,突然间豁然开朗.再次写下自己对于STL空间配置器的一点点理解. 要了解空间配置器,有一张图是必看的: 这张图是一级空间配置器宇二级空间配置器的封装方式与调用.从此图我们们可以看到其实空间配置器是分为两级的,而这里所谓的两级并没有高低之分

第一级配置器 --—_ _malloc_alloc_template剖析

程通常被称为new-handler. 注:设计"内存不足处理例程"是客户端的责任,设定"内存不足处理例程"也是客户端的责任. 版权声明:本文为博主原创文章,未经博主允许不得转载.

c++ stl 内存配置器

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

STL源码分析--第二级空间配置器

本文讲解SGI STL空间配置器的第二级配置器. 相比第一级配置器,第二级配置器多了一些机制,避免小额区块造成内存的碎片.不仅仅是碎片的问题,配置时的额外负担也是一个大问题.因为区块越小,额外负担所占的比例就越大. 额外负担是指动态分配内存块的时候,位于其头部的额外信息,包括记录内存块大小的信息以及内存保护区(判断是否越界).要想了解详细信息,请参考MSVC或者其他malloc实现. SGI STL第二级配置器具体实现思想 如下: 如果要分配的区块大于128bytes,则移交给第一级配置器处理.