STL学习整理(三)

SGI版本的空间配置器解析

SGI 版本一级空间配置器剖析

具备次配置力的,空间利用效率很高,能够有效利用碎片化的空间。设置了两个层级的空间配置器,一级空间配置器用于申请>128,二级空间配置器用于申请<128。
C++处理申请空间失败的机制:
set_new_handler(函数指针),当程序内存申请失败的时候则会执行set_new_handler函数,看它能不能释放一些内存。

#ifndef _STL_ALLOC_H_
#define _STL_ALLOC_H_

#if 0
#include<new>
#define __THROW_BAD_ALLOC throw bad_alloc
#elif !defined (__THROW_BAD_ALLOC)
#include<iostream.h>
#include<malloc.h>
#include<stdlib.h>
#define __THROW_BAD_ALLOC cerr<<"Out Of Memory."<<endl; exit(1)
#endif
//一级空间配置器的实现
template<int inst>
class __malloc_alloc_template
{
public:
    //申请空间
    static void* allocate(size_t n)
    {
        void *result = malloc(n);//第一级配置直接使用malloc()
        //以下无法满足需求时,改用oom_malloc()
        if(0 == result)
            result = oom_malloc(n);
        return result;
    }
    static void  deallocate(void *p, size_t)
    {
        free(p);//第一级配置器直接使用free()
    }
    static void  reallocate(void *p, size_t old_sz, size_t new_sz)
    {
        void *result = realloc(p, new_sz);//第一级配置器直接使用realloc()
        //以下无法满足需求时,改用oom_realloc()
        if(0 == result)
            result = oom_realloc(p, new_sz);
        return result;
    }
public:
    //以下仿真C++的set_malloc_handler()
    //可以通过它指定你自己的out-of-memory handler
    static void(* set_malloc_handler(void(*f)()))()
    {
        void(*old)() = __malloc_alloc_oom_handler;
        __malloc_alloc_oom_handler = f;
        return old;
    }
private:
    //以下都是函数指针,所代表的函数将用来处理内存不足的情况
    //oom:out of memory
    static void* 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;
        }
    }
    static void* oom_realloc(void *p, size_t new_sz)
    {
        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);
        }
    }
    static void(*__malloc_alloc_oom_handler)();//函数指针
};
//静态成员的初始化,放到类外来初始化
//初始值客户端为0,有待客户端设置
template<int inst>
void(*__malloc_alloc_template<inst>::__malloc_alloc_oom_handler)() = 0;
//注意,以下参数直接将参数inst 指定为0
typedef __malloc_alloc_template<0> malloc_alloc;
  1. 第一级配置器以malloc(),free(),realloc()等C函数执行实际的内存配置、释放、重配置操作,并出现类似C++new-handler的机制。他不能直接运用C++ new-handler的机制,因为它并非使用::operator new来配置内存。
  2. 内存配置无法满足的时候,你可以调用一个指定的函数。::operator new无法完成任务的时候,在丢出异常状态之前,会先调用客端指定的处理例程。
  3. 底层用的C函数, 可能是历史原因
  4. SGI的第一级配置器的allocate()和realloc()都是在调用malloc()和realloc()不成功后,改调用oom_malloc()和oom_realloc()。后者都有内循环,不断调用“内存不足处理例程”,希望在某次调用之后,获得足够的内存而圆满完成任务。如果没有设定内存不足处理例程,那么就会调用__THROW_BAD_ALLOC,抛出"Out Of Memory."。
SGI 版本二级空间配置器剖析
#ifndef _STL_ALLOC_H_
#define _STL_ALLOC_H_

#if 0
#include<new>
#define __THROW_BAD_ALLOC throw bad_alloc
#elif !defined (__THROW_BAD_ALLOC)
#include<iostream.h>
#include<malloc.h>
#include<stdlib.h>
#define __THROW_BAD_ALLOC cerr<<"Out Of Memory."<<endl; exit(1)
#endif
//一级空间配置器的实现
template<int inst>
class __malloc_alloc_template
{
public:
	//申请空间
	static void* allocate(size_t n)
	{
		void *result = malloc(n);
		if(0 == result)
			result = oom_malloc(n);//out of memory前提下,调用的一个补救函数
		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 = oom_realloc(p, new_sz);
		return result;
	}
public:
	//函数名,返回的是无返回值、无参数的函数指针
	//等效于static void()* set_malloc_handler(void(*f)())
	static void(* set_malloc_handler(void(*f)()))()
	{
		void(*old)() = __malloc_alloc_oom_handler;
		__malloc_alloc_oom_handler = f;
		return old;
	}
private:
	static void* 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;   
		}
	}
	static void* oom_realloc(void *p, size_t new_sz)
	{
		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);
		}
	}
	static void(*__malloc_alloc_oom_handler)();//函数指针成员
};
//静态成员的初始化,放到类外来初始化
template<int inst>
void(*__malloc_alloc_template<inst>::__malloc_alloc_oom_handler)() = 0;

typedef __malloc_alloc_template<0> malloc_alloc;//给类名重新起名

//
//用枚举来定义,相比于宏它会做类型检查
enum {__ALIGN = 8};
enum {__MAX_BYTES = 128};
enum {__NFREELISTS = __MAX_BYTES / __ALIGN};

template<bool threads, int inst>
class __default_alloc_template
{
public:
	static void* allocate(size_t n)
	{
		if(n > __MAX_BYTES)//大于128
		{
			return malloc_alloc::allocate(n);//调用一级空间配置器
		}

		obj **my_free_list;
		obj *result;

		my_free_list = free_list + FREELIST_INDEX(n);
		result = *my_free_list;
		if(result == 0)
		{
			void *r = refill(ROUND_UP(n));
			return r;
		}
		//如果有空间的话,my_free_list移动到链表的下一端,返回result
		*my_free_list = result->free_list_link;//更改*my_free_list的指向
		return result;

	}
	static void  deallocate(void *p, size_t n);
	static void* reallocate(void *p, size_t old_sz, size_t new_sz);
protected:
	static void* refill(size_t n);
	static char* chunk_alloc(size_t size, int &nobjs);
private:
	//将byte上升为__ALIGN的倍数
	//该方法只能够上升为2的幂次
	static size_t ROUND_UP(size_t bytes)
	{
		return ((bytes + __ALIGN-1) & ~(__ALIGN-1));
	}
	//核心
	union obj
	{
		union obj *free_list_link;
		char client_data[1];
	};
	static size_t FREELIST_INDEX(size_t bytes)
	{
		return ((bytes + __ALIGN-1)/__ALIGN-1);//先除,再来减一
	}
private:
	static obj* free_list[__NFREELISTS];
	static char *start_free;
	static char *end_free;
	static size_t heap_size;
};

template<bool threads, int inst>
char* __default_alloc_template<threads, inst>::start_free = 0;
template<bool threads, int inst>
char* __default_alloc_template<threads, inst>::end_free = 0;
template<bool threads, int inst>
size_t __default_alloc_template<threads, inst>::heap_size = 0;
//类型是obj*
template<bool threads, int inst>
__default_alloc_template<threads, inst>::obj* 
__default_alloc_template<threads, inst>::free_list[__NFREELISTS] = 
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

template<bool threads, int inst>
void* __default_alloc_template<threads, inst>::refill(size_t n)
{
	int nobjs = 20;
	char *chunk = chunk_alloc(n, nobjs);//此处的nobjs是个引用,从空间中拿了20个8出来

	obj ** my_free_list;
	obj *result;
	obj *current_obj, *next_obj;
	//如果只能获得一个区块,这个区块就分配给调用者用,free list 无新节点
	if(nobjs == 1)
		return chunk;
	//否则准备调整free list,纳入新节点
	my_free_list = free_list + FREELIST_INDEX(n);
	//以下在chunk 空间内建立free list
	result = (obj*)chunk;

	*my_free_list = next_obj = (obj*)(chunk + n);//+n因为是一块一块(都是8的倍数)的内存
	//以下将free list的各个节点串接起来
	for(int i=1; ; i++)//将剩下的19个形成free list
	{
		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;
}
//用内存池技术
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)//内存池剩余空间,完全满足需求
	{
		result = start_free;
		start_free += total_bytes;//将申请的空间一分为二(拿160的空间到底干什么呢?),start_free和end_free之间维持的是内存池的水量
		return result;//如果有空间的话,就从这里返回
	}
	else if(bytes_left >= size)//内存池剩余空间不能完全满足需求量,但足够供应一个(含)以上的区块
	{
		nobjs = bytes_left / size;
		total_bytes = size * nobjs;
		result = start_free;
		start_free += total_bytes;
		return result;
	}
	else
	{
		size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);//为什么要加整个空间的1/16???减少malloc的次数,实际上是一个经验值
		//整体只会形成一个内存池,前面用不完,后面就不会生成
		if(bytes_left > 0)//在申请新的空间之前,处理剩余的空间,找到大小一样的free list做一个头插
		{
			obj **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)
		{
			int i;
            obj ** my_free_list, *p;
            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));
                }
			}

			end_free = 0;	// In case of exception.
            start_free = (char *)malloc_alloc::allocate(bytes_to_get);
		}
		//申请成功的情况
		heap_size += bytes_to_get;
		end_free = start_free + bytes_to_get;
		return chunk_alloc(size, nobjs);//递归调用之后,就有空间来分配了
	}
}



#ifdef __USE_MALLOC
typedef __malloc_alloc_template<0> malloc_alloc;
typedef malloc_alloc alloc;
#else
typedef __default_alloc_template<0,0> alloc;
#endif

template<class T, class Alloc>
class simple_alloc
{
public:
	static T * allocate(size_t n)
	{return 0==n ? 0 : (T*)Alloc::allocate(n * sizeof(T));}
	static T * allocate(void)
	{return (T*)Alloc::allocate(sizeof(T));}
	static void deallocate(T *p, size_t n)
	{if(0 != n)
		Alloc::deallocate(p, n*sizeof(T));
	}
	static void deallocate(T *p)
	{Alloc::deallocate(p, sizeof(T));}
};

#endif /* _STL_ALLOC_H_ */

PS: 二级空间配置器还是比较复杂的,最好可以把代码放到IDE中运行一下,然后去一步一步追踪过程,这样才能够更加深刻的理解其过程。

volatile关键字
告诉编译器,参与运算的时候,不要直接从寄存器取值(而是每次都要从内存中加载),防止内存中的值已经变化而发生错误。一般在嵌入式编程中比较常见。
内存池技术的结构以及头插的过程示意图
二级配置器的内存池结构

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值