(一)STL剖析——空间配置器

Alloc.h

//Alloc.h负责内存空间的配置与释放
//Construct.h负责对象内容的构造与析构
//这两个头文件在memory文件中包含
#pragma once

typedef void(*HANDLER_FUNC)();

//一级空间配置器
template <int inst>
class __MallocAllocTemplate 
{
public:
	static void* Allocate(size_t size)//静态成员函数
	{
		void* result = malloc(size);
		if (result == NULL){
			result = __OomMalloc(size);
		}
		return result;
	}
	static void Deallocate(void* ptr)
	{
		free(ptr);
	}
	static void* Reallocate(void* ptr,size_t size)
	{
		void* result = realloc(ptr,size);
		if (result == NULL){
			result = __OomRealloc(ptr,size);
		}
		return result;
	}

	//分配内存失败处理函数的句柄函数指针,初值为NULL,客户端可以设定
	static void(*__MallocAllocOomHandler)();
private:
	static void* __OomMalloc(size_t size);
	static void* __OomRealloc(void *p,size_t size);

//	模仿C++的set_new_handler();
//	static void(*SetMallocHandler(void(*f)()))(){//函数
	static HANDLER_FUNC SetMallocHandler(HANDLER_FUNC f){
		HANDLER_FUNC old = __MallocAllocOomHandler;
		__MallocAllocOomHandler = f;
		return old;
	}
};

template<int inst>         
void (* __MallocAllocTemplate<inst>::__MallocAllocOomHandler)() = NULL;

template<int inst>
void* __MallocAllocTemplate<inst>::__OomMalloc(size_t size)
{
	void(*myMallocHandler)();
	void *result;

	for (;;) {
		myMallocHandler = __MallocAllocOomHandler;
		if (0 == myMallocHandler) {//没有自定义内存异常处理函数,STL中抛异常
			std::cout << "out of memory" << std::endl;
			exit(-1);
		}
		(*myMallocHandler)();//调用自定义内存异常处理函数
		result = malloc(size);
		if (result) return(result);
	}
}
template<int inst>
void* __MallocAllocTemplate<inst>::__OomRealloc(void *ptr,size_t size)
{
	void(*myMallocHandler)();
	void *result;

	for (;;) {
		myMallocHandler = __MallocAllocOomHandler;
		if (0 == myMallocHandler) { 
			std::cout << "out of memory" << std::endl;
			exit(-1);
		}
		(*myMallocHandler)();
		result = realloc(ptr, size);
		if (result) 
			return (result);
	}
}

template<class T, class Alloc>
class SimpleAlloc 
{
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 *ptr, size_t n)
	{
		if (0 != n) Alloc::Deallocate(ptr, n * sizeof (T));
	}
	static void Deallocate(T *ptr)
	{
		Alloc::Deallocate(ptr, sizeof (T));
	}
};

//template <class Alloc>
//class debug_alloc {}

# ifdef __USE_MALLOC

typedef __MallocAllocTemplate<0> alloc;

# else
//二级空间配置器
enum{__ALIGN=8};
enum{__MAXBYTES=128};
enum{ __NFREELISTS = __MAXBYTES/__ALIGN};//自由链表的个数

//第一个参数用于多线程,第二个参数没有派上用场
template <bool threads, int inst>
class __DefaultAllocTemplate 
{
private:
	union Obj{
		union Obj* freeLinkList;
		char clientData[1];//给用户使用
	};
	static size_t FREELIST_INDEX(size_t size)//根据size决定使用几号自由链表
	{
		return ((size + (__ALIGN - 1)) / __ALIGN - 1);
	}
	static size_t ROUND_UP(size_t size) {//将size往上调整为8的倍数
		return ((size + __ALIGN - 1) & ~(__ALIGN - 1));
	}
public:
	static void* Allocate(size_t size);
	static void Deallocate(void* ptr,size_t size);
	static void* Reallocate(void* p,size_t size);
private:
	static char* ChunkAlloc(size_t size, size_t& Objs);
	static void* Refill(size_t size);
public:
	static char* _start;//内存池
	static char* _end;
	static size_t _heapSize;

	static Obj* _freeList[__NFREELISTS];//指向自由链表
};
template <bool threads, int inst>
char* __DefaultAllocTemplate<threads, inst>::_start = 0;
template <bool threads, int inst>
char* __DefaultAllocTemplate<threads, inst>::_end = 0;
template <bool threads, int inst>
size_t __DefaultAllocTemplate<threads, inst>::_heapSize = 0;
template <bool threads, int inst>
typename __DefaultAllocTemplate<threads, inst>::Obj* __DefaultAllocTemplate<threads, inst>::_freeList[__NFREELISTS] =		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

typedef __DefaultAllocTemplate<true, 0> alloc;

template <bool threads, int inst>
void* __DefaultAllocTemplate<threads, inst>::Allocate(size_t size)
{
	if (size > __MAXBYTES){
		return __MallocAllocTemplate<0>::Allocate(size);
	}
	size_t index = FREELIST_INDEX(size);
	Obj* myFreeList = _freeList[index];//STL中用的是二级指针
	if (myFreeList != NULL){//自由链表对应位置挂有内存块
		_freeList[index] = myFreeList->freeLinkList;
		return myFreeList;
	}
	//自由链表对应位置没有内存块,到内存池取
	void *result = Refill(ROUND_UP(size));
	return result;
}
template <bool threads, int inst>
void __DefaultAllocTemplate<threads, inst>::Deallocate(void* ptr,size_t size)
{
	if (size > __MAXBYTES){//调用一级空间配置器
		__MallocAllocTemplate<0>::Deallocate(ptr);
	}
	else{
		size_t index = FREELIST_INDEX(size);
		((Obj*)ptr)->freeLinkList = _freeList[index];
		_freeList[index] = (Obj*)ptr;
	}
}
template <bool threads, int inst>
void* __DefaultAllocTemplate<threads, inst>::Reallocate(void* p, size_t size)
{

}
//size此时已调至8的倍数,第二个参数必须是引用
template <bool threads, int inst>
char*  __DefaultAllocTemplate<threads, inst>::ChunkAlloc(size_t size, size_t& Objs)
{
	size_t TotalBytes =Objs*size;
	size_t LeftBytes = _end - _start;
	char* result=NULL;
	if (TotalBytes <= LeftBytes){//内存池有满足需求的内存
		result = _start;
		_start += TotalBytes;
	}
	else{
		if (LeftBytes < size){//内存池没有足够大,调用一级空间配置器
			size_t requSize = 2 * TotalBytes + ROUND_UP(_heapSize >> 4);
			int index = 0;
			Obj* myfreeList=NULL;
			if (LeftBytes>0){//内存池还有一些零头,挂到相应的位置(它一定是8的倍数)
				myfreeList = (Obj*)_start;
				index = FREELIST_INDEX(LeftBytes);
				myfreeList->freeLinkList = _freeList[index];
				_freeList[index] = myfreeList;
			}
			//调用一级空间配置器配置内存池
			_start = (char*)__MallocAllocTemplate<0>::Allocate(requSize);
			if (_start == 0){//一级空间配置器分配失败,到自由链表中试着取比需要大小内存更大的内存
				index = FREELIST_INDEX(size);

				for (index; index < __NFREELISTS; ++index){
					if (_freeList[index] != NULL){
						_start = (char*)_freeList[index];
						_freeList[index]=((Obj*)_start)->freeLinkList;
						_end = _start + (index+1)*__ALIGN;
						return(	ChunkAlloc(size, Objs));
					}
				}
				_end = NULL;//没有任何空间可用

				_start = (char*)__MallocAllocTemplate<0>::Allocate(requSize);
			}
			_end = _start + requSize;
			_heapSize += requSize;
			return (ChunkAlloc(size,Objs));
		}
		else{//内存池能满足一个空间的大小
			Objs = LeftBytes / size;
			TotalBytes = Objs * size;
			result = _start;
			_start += TotalBytes;
		}
	}
	return result;
}

template <bool threads, int inst>
void* __DefaultAllocTemplate<threads, inst>::Refill(size_t size)
{
	size_t Objs = 20;//想申请20个对象的大小挂到自由链表中
	char* chunk = ChunkAlloc(size,Objs);//拿到一大块的内存准备往挂到自由链表

	if (Objs == 1)
		return chunk;

	Obj* result = (Obj*)chunk;
	Obj* curObj = NULL;
	Obj* nextObj = NULL;
	size_t index = FREELIST_INDEX(size);
	_freeList[index] = nextObj = (Obj*)(chunk + size);
	for (size_t i = 0; i < Objs-2; ++i){
		curObj = nextObj;
		nextObj = curObj + 1;
		curObj->freeLinkList = nextObj;
	}
	curObj->freeLinkList = NULL;

	return result;
}
#endif

Test.cpp

#include "Alloc.h"

void Testalloc1()
{
	/*char* ptr1 = new char;
	char* ptr2 = new char[4];*/
	std::cout << " 测试调用一级配置器分配内存 " << std::endl;
	char*p1 = SimpleAlloc< char, alloc>::Allocate(129);
	p1 = SimpleAlloc< char, alloc>::Allocate(8);
	SimpleAlloc<char, alloc>::Deallocate(p1, 129);

	// 测试调用二级配置器分配内存    
	std::cout << " 测试调用二级配置器分配内存 " << std::endl;
	char*p2 = SimpleAlloc< char, alloc>::Allocate(128);
	char*p3 = SimpleAlloc< char, alloc>::Allocate(128);
	char*p4 = SimpleAlloc< char, alloc>::Allocate(128);
	char*p5 = SimpleAlloc< char, alloc>::Allocate(128);
	SimpleAlloc<char, alloc>::Deallocate(p2, 128);
	SimpleAlloc<char, alloc>::Deallocate(p3, 128);
	SimpleAlloc<char, alloc>::Deallocate(p4, 128);
	SimpleAlloc<char, alloc>::Deallocate(p5, 128);
	for (int i = 0; i < 21; ++i){
		printf(" 测试第%d次分配 \n", i + 1);
		char*p = SimpleAlloc< char, alloc>::Allocate(128);
	}
}

void TestAlloc2()
{
	// 测试特殊场景 
	std::cout<<" 测试内存池空间不足分配个 "<<std::endl ;     // 8*20->8*2->320     
	char*p1 = SimpleAlloc< char, alloc>::Allocate (8);
	char*p2 = SimpleAlloc< char, alloc>::Allocate(8);
	std::cout << " 测试内存池空间不足,系统堆进行分配 " << std::endl;   
	char*p3 = SimpleAlloc< char, alloc>::Allocate(12);
}

void TestAlloc3()
{
	std::cout << " 测试系统堆内存耗尽 " << std::endl;
	SimpleAlloc<char, alloc>::Allocate(1024 * 1024 * 1024); 
	SimpleAlloc<char, alloc>::Allocate(1024 * 1024);
	// 不好测试,说明系统管理小块内存的能力还是很强的。     
	for (int i = 0; i < 100000; ++i ){
		char*p1 = SimpleAlloc< char, alloc>::Allocate (128);  
	} 

}
void TestAlloc()
{
	//TestAlloc1();
	//TestAlloc2();
	TestAlloc3();
}
时间: 2024-08-10 19:17:59

(一)STL剖析——空间配置器的相关文章

STL源码剖析 — 空间配置器(allocator)

前言 以STL的实现角度而言,第一个需要介绍的就是空间配置器,因为整个STL的操作对象都存放在容器之中. 你完全可以实现一个直接向硬件存取空间的allocator. 下面介绍的是SGI STL提供的配置器,配置的对象,是内存.(以下内容来自<STL源码剖析>) 空间配置器的标准接口 根据STL的规范,allocator的必要接口 各种typedef 1 allocator::value_type 2 allocator::pointer 3 allocator::const_pointer 4

STL源码剖析 --- 空间配置器 std::alloc

STL是建立在泛化之上的.数组泛化为容器,参数化了所包含的对象的类型.函数泛化为算法,参数化了所用的迭代器的类型.指针泛化为迭代器,参数化了所指向的对象的类型.STL中的六大组件:容器.算法.迭代器.配置器.适配器.仿函数. 这六大组件中在容器中分为序列式容器和关联容器两类,正好作为STL源码剖析这本书的内容.迭代器是容器和算法之间的胶合剂,从实现的角度来看,迭代器是一种将operator*.operator->.operator++.operator-等指针相关操作予以重载的class tem

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

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

STL源码剖析--空间配置器

STL的设计非常巧妙,组件间互取短长,形成了一个世界,这是这个世界里的组件: 1. containers(容器):所谓容器,是指存放数据的地方,将数据以一定的方法组织存放.根据不同的组织方式,可以把容器分为顺序容器,如vector.deque.list,关联容器,如set.map.Container是一种class template. 2. algorithm(算法):各种常用不常用的算法如sort.copy.search等等.algorithm是一种function template. 3.

STL源码剖析——空间配置器Allocator#2 一/二级空间配置器

上节学习了内存配置后的对象构造行为和内存释放前的对象析构行为,在这一节来学习内存的配置与释放. C++的内存配置基本操作是::operator new(),而释放基本操作是::operator delete().这两个全局函数相当于C的malloc() 和free() 函数.而SGI正是以malloc() 和free() 完成内存的配置与释放. 考虑到小型区块可能造成的内存破碎问题,SGI设计了两级的空间配置器.第一级直接使用malloc() 和free() ,而第二级则视情况采用不同的策略:当

STL 之 空间配置器(allocator)

一.SGI 标准的空间配置器,std::allocator SGI也定义了一个符合部分标准,名为allocator的配置器,但是它自己不使用,也不建议我们使用,主要原因是效率不佳. 它只是把C++的操作符::operator new和::operator delete做了一层简单的封装而已. 二.SGI 特殊的空间配置器,std::alloc 由于SGI 标准的空间配置器只是把C++的操作符::operator new和::operator delete做了一层简单的封装,没有考虑到任何效率上的

C++标准库——STL之空间配置器

声明:源码同<STL源码剖析>(侯捷) STL: C++标准的模板库,通用性高. 常见的数据结构封装. 提供常用的通用算法. STL六大组件: 容器      算法       迭代器       仿函数(函数对象)     适配器      配置器 空间配置器的作用: 1.提高代码复用率,功能模块化. 2.减少内存碎片问题. 比如我们list是链式结构,如果其中的成员是一个个new出来的,我们知道new的底层实现是malloc,同时也必须清楚,当malloc一块40字节空间时,操作系统为了能

STL源码剖析——空间配置器Allocator#3 自由链表与内存池

上节在学习第二级配置器时了解了第二级配置器通过内存池与自由链表来处理小区块内存的申请.但只是对其概念进行点到为止的认识,并未深入探究.这节就来学习一下自由链表的填充和内存池的内存分配机制. refill()函数——重新填充自由链表 前情提要,从上节第二级配置器的源码中可以看到,在空间配置函数allocate()中,当所需的某号自由链表为空时,才会调用refill()函数来填充链表.refill()函数默认申请20块区块的内存(5行),但所得内存不一定就是20块,要看当前内存池的剩余情况和堆容量的

STL 源码剖析读书笔记一:空间配置器

1. STL 的空间配置器 STL 空间配置器在运用的角度来说,是最不需要介绍的,它总是隐藏在一切组件背后.但若以 STL 的实现角度而言,第一个需要理解的就是空间配置器. 根据 STL 规范,以下是 allocator 的必要接口: allocator::value_type allocator::pointer allocator::const_pointer allocator::reference allocator::const_reference allocator::size_ty