1空间配置器

Posted ho966

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了1空间配置器相关的知识,希望对你有一定的参考价值。

头文件<memory>

1.1 std::allocate、std::deallocate   (通过::operator new ::operator delete封装实现)

1.2 std::construct、std::destory    (通过placement new 和调用析构函数实现)

 

template<class T1, class T2>
inline void h_construct(T1* p, const T2& value)
{
	new(p) T1(value);
}
template<class T>
inline void h_destroy(T* pointer)
{
	pointer->~T();
}

 

1.3 std::alloc

STL考虑到小型内存区块问题,存在两个问题:1) 造成内存碎片

                     2)造成额外负担(索取内存块,需要额外空间用于管理,区块越小,额外负担所占比例越大)

设计了双层级配置器,第一级配置直接使用malloc()free();第二级配置器则视情况采用不同的策略,当配置区大于128bytes时,直接调用第一级配置器;当配置区块小于128bytes时,便不借助第一级配置器,而使用一个memory pool来实现。究竟是使用第一级配置器还是第二级配置器,由一个宏定义来控制。SGI STL中默认使用第二级配置器。

技术图片

 

第二级配置器维护16free-lists, 各自管理大小为8,16,24...120,128bytes的小额区块。

技术图片

 

free-lists节点结构如下,通过union的方式解决了管理节点指针的额外负担,第一个字段构成链表,第二个字段指向实际区块

union obj {
union obj* free_list_link;
char  client_data[1];    /* The client sees this.        */
};

类构造

class h_alloc
{
private:
	enum {
		ALIGN = 8,					// 对齐边界
		MAX_BYTES = 128,				// 最大上限
		NFREELISTS = MAX_BYTES/ ALIGN	// 个数
	};

	union obj {					// 区块对应自由链表的节点结构
		union obj* free_list_link;
		char  client_data[1];    /* The client sees this.        */
	};
	
private:
	static size_t round_up(size_t bytes) // 对齐函数
	{
		return (((bytes)+(size_t)ALIGN - 1) & ~((size_t)ALIGN - 1));
	}

	// 计算使用第n号链表,n从0开始
	static  size_t freeList_index(size_t bytes)	
	{
		return (((bytes)+(size_t)ALIGN - 1) / (size_t)ALIGN - 1);
	}

	// 返回一个大小为n的对象,并可能加入大小为n的其他区块到free_list
	static void*  refill(size_t n);  

	// 配置个大块空间,可容纳nobjs个size大小的区块
	static char*  chunk_alloc(size_t size, int& nobjs);

public:
	static void* allocate(size_t n);
	static void  deallocate(void* p, size_t n);
	//static void* reallocate(void* p, size_t old_sz, size_t new_sz);

private:
	static obj* volatile m_free_list[NFREELISTS];// 16个 free-lists
	static char*  m_start_free;	 //内存池起始位置,只在chunk_alloc()中变化
	static char*  m_end_free;	 //内存池结束位置,只在chunk_alloc()中变化
	static size_t m_heap_size;
};

char*  h_alloc::m_start_free = 0;
char*  h_alloc::m_end_free = 0;
size_t h_alloc::m_heap_size = 0;
typename h_alloc::obj* volatile h_alloc::m_free_list[NFREELISTS];

1) allocate()函数

void* h_alloc::allocate(size_t n)
{
	obj* volatile * my_free_list;
	obj* result;
	if (n > MAX_BYTES) //大于128字节调用第一级配置器
	{
		return (malloc(n));
	}
	else
	{
		//寻找16个自由链表中合适的一个
		my_free_list = m_free_list + freeList_index(n);
		result = *my_free_list;
		if (result == 0)
		{
			// 没找到可用的free_list, 重新填充free_list
			void *r = refill(round_up(n));
			return r;
		}
		// 调整free_list
		*my_free_list = result->free_list_link;
		return result;
	}
}

技术图片

2) deallocate()

void  h_alloc::deallocate(void* p, size_t n)
{
	obj *q = (obj*)p;
	obj* volatile * my_free_list;
	if (n > MAX_BYTES)
	{
		free(q);
		return;
	}
	// 寻找对应的free_list
	my_free_list = m_free_list + freeList_index(n);

	// 调整free_list, 回收区块
	q->free_list_link = *my_free_list;
	*my_free_list = q;
}

3) refill()

填充自由链表用,缺省一次取20个新节点,具体看内存池大小

void* h_alloc::refill(size_t n)
{
	int nobjs = 20; //默认取20个节点
	char* chunck = chunk_alloc(n, nobjs);//从内存池申请内存
	if (nobjs == 1)
	{
		return chunck;   // 如果只申请到一个节点,不用调整free_list,直接返回
	}
	obj* volatile * m_free_list = m_free_list + freeList_index(n); //找到n号链表
	obj* result = (obj*)chunck; // 保存要返回的结果

	obj* next_obj = (obj*)(chunck + n);
	*m_free_list = next_obj; //链表第一个节点
	
	// 串联各个节点,第一个节点已经返回出去了
	obj* current_objs;
	for (int i = 1; ;i++)
	{
		current_objs = next_obj;

		if (nobjs - 1 == i)
		{
			current_objs->free_list_link = 0;
			break;
		}
		else
		{
			next_obj = (obj*)(next_obj + n);
			current_objs->free_list_link = next_obj;
		}
	}
	return result;
}

4) chunk_alloc()

从内存池取空间给refill函数,内存池不够,从堆空间获取内存给内存池

char*  h_alloc::chunk_alloc(size_t size, int& nobjs)
{
	size_t total_bytes = size * nobjs;
	size_t left_bytes = m_end_free - m_start_free;
	char* result;
	if(left_bytes > total_bytes) //内存池剩余空间满足需求量
	{
		result = m_start_free;
		m_start_free += total_bytes;
		return result;
	}
	else if (left_bytes > size) //内存池剩余空间不满足需求量,但是足够一个以上的区块
	{
		nobjs = left_bytes / size;
		total_bytes = nobjs * size;
		result = m_start_free;
		m_start_free += total_bytes;
		return result;
	}
	else //内存池大小不够一个区块
	{
		size_t bytes_to_get = 2 * total_bytes + round_up(m_heap_size >> 4);
		//size_t bytes_to_get = 2 * total_bytes;

		if (left_bytes > 0) //残余零头不浪费
		{
			obj* volatile * m_free_list = m_free_list + freeList_index(left_bytes); //找到n号链表
			((obj*)m_start_free)->free_list_link = *m_free_list;
			*m_free_list = (obj*)m_start_free;
		}
		//申请堆空间
		m_start_free = (char*)malloc (bytes_to_get);
		if (0 == m_start_free)
		{
			printf("chunk_alloc malloc failed
");
			//申请失败处理,省略。。。
		}
		m_heap_size += bytes_to_get;
		m_end_free = m_start_free + bytes_to_get;
		//递归调用,修正nobjs
		return (chunk_alloc(size, nobjs));
	}
}

 

  

 

  

 

 

以上是关于1空间配置器的主要内容,如果未能解决你的问题,请参考以下文章

为啥 gl_Color 不是片段着色器的内置变量?

VSCode自定义代码片段——CSS选择器

VSCode自定义代码片段6——CSS选择器

std::alloc 二级配置器

SGI版本空间配置器

STL空间配置器解析和实现