STL源码剖析-2

2.空间配置器(allocator)

代码

STL所有的对象都存放于容器中,空间的配置由allocator负责

STL(SGI STL)提供了两个空间配置器

  • std::alloc默认使用的,做了很多的优化
  • std::allocator 效率不佳,仅仅是对::operator new::operator delete的简单封装
2.1 std::allocator (大致如下)
// defalloc.h
#ifndef DEFALLOC_H
#define DEFALLOC_H

#include<new>
#include<cstddef>
#include<cstdlib>
#include<climits>
#include<iostream>

using namespace std;

template <typename T>
inline T* allocate(ptrdiff_t size, T*){
	set_new_handler(0);
	T* tmp = (T*) (::operator new((size_t) (size * sizeof(T))));
	if (!tmp) {
		cerr<<"out of memory"<<endl;
		exit(1);
	}
	return tmp;
	
}

template <typename T>
inline void deallocate(T* buffer){
	::operator delete(buffer);
}

template <typename T>
class Allocator
{
	public:
		typedef T value_type;
		typedef T* pointer;
		typedef const T* const_pointer;
		typedef T& reference;
		typedef const T& const_reference;
		typedef size_t size_type;
		typedef ptrdiff_t difference_type;

		pointer allocate(size_type n){
			return ::allocate((difference_type) n, (pointer) 0);
		}

		void deallocate(pointer p) {::deallocate*p;}

		pointer address(reference x) {return (pointer) &x;}

		const_pointer const_address (const_reference x) {return (const_pointer) &x;}
		
		size_type init_page_size(){
			return max(size_type(1), size_type(4096/sizeof(T)));
		}

		size_type max_size(){
			return max(size_type(1), size_type(UINT_MAX/sizeof(T)));
		}

};

template <>
class Allocator <void>
{
	public:
		typedef void* pointer;	
};


#define DEFALLOC_H
#endif 
复制代码
2.2 std::alloc

将new和delete各自的两步操作分开;可以减少不必要的配置和释放

  • stl_alloc.h 负责内存空间的配置和释放
  • stl_construct.h 负责对象的构造和析构
  • stl_uninstialized.h定义了一些全局处理内存的函数

stl_construct.h的大致实现

// stl_construct.h 部分类似代码,泛化的construct, 和特化了char, wchar_t, pointer,以及trival的 destroy
// 当迭代器版本的destroy,范围很大,但是每个对象的得析构函数都没有必要时,就不需要每个都调用析构函数
// traits相关,第三章会介绍


#ifndef STL_CONSTRUCT_H
#define STL_CONSTRUCT_H

#include<new> // 为了使用placement new, new的一个重载

template <typename T1, typename T2>
inline void construct(T1 *p, const T2& value){
	new (p) T1(value);
}

template<typename T>
inline void destroy(T *pointer){
	pointer->~T();
}

template<typename ForwardIterator>
inline void destroy(ForwardIterator first, ForwardIterator last){
	__destroy(first, last, value_type(first));
}
// value_type 见3.6节,萃取出迭代器的value_type

template<typename ForwardIterator, typename T>
inline void __destroy(ForwardIterator first, ForwardIterator last, T*){
	typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
	__destroy_aux(first, last, trivial_destructor())
}

template<typename ForwardIterator>
inline void __destroy_aux(ForwardIterator first, ForwardIterator last, __false_type) {
	for (;first<last;++first)
		destroy(*first);
}

template<typename ForwardIterator>
inline void __destroy_aux(ForwardIterator , ForwardIterator , __true_type){}

template<>
inline void destroy(char*, char*) {}

template<>
inline void destroy(wchar_t*, wchar_t*) {}

#endif
复制代码

stl_alloc.h设计策略

  • 向system heap请求空间,考虑多线程.考虑内存不足,考虑内存碎片问题
  • 双层级配置器
    • 第一级直接调用malloc, free(当配置区块大于128bytes时)
    • 第二级视情况不同策略(如果定义了__USE_MALLOC只会用第一级),由内存池管理,维护了16个链表对应不同可用大小的空间
      • 因为对应小内存块,内存块越小,用于存储管理信息的空间(链表指针)占比会显得越大(STL通过 union结构解决(见下面代码的 template<bool threads, int inst> typename __default_alloc_template<threads, inst>::obj,未分配时是指针,分配出去时变为相应数据)

stl_alloc.h的大致实现

// 由于并非直接调用new,会类似实现c++ new handler机制,类似set_new_handler(0)
#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>
#	define __THROW_BAD_ALLOC std::cerr<<"out of memory"<<std::endl;
#endif
#include<cstdlib>
#include<new>

// ***一级malloc****
template<int inst>
class __malloc_alloc_template{
	private:
		static void *oom_malloc(size_t);
		static void *oom_realloc(void *, size_t);
		static void (* __malloc_alloc_oom_handler) (); // 实现c++ new handler
	public:
		static void *allocate(size_t n) {
			void *result = malloc(n);
			if (result==0) result=oom_malloc(n);
			return result;
		}

		static void deallocate(void *p, size_t){
			free(p);
		}

		static void *reallocate(void *p, size_t, size_t new_sz){
			void *result = realloc(p, new_sz);
			if (result==0) result=oom_realloc(p, new_sz);
			return result;
		}

		static void (*set_malloc_handler(void (*f)())) (){ //为啥不typedef 下?
			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; 

template<int inst>
void *__malloc_alloc_template<inst>::oom_malloc(size_t n) {
	void (*my_malloc_handler)();
	void *result;
	for (;;){
		my_malloc_handler = __malloc_alloc_oom_handler;   //内存不足处理
		if (my_malloc_handler==0) {__THROW_BAD_ALLOC;}
		my_malloc_handler();
		result = malloc(n);
		if (result) return result;
	}
}
template<int inst>
void *__malloc_alloc_template<inst>::oom_realloc(void *p, size_t n) {
	void (*my_malloc_handler)();
	void *result;
	for (;;){
		my_malloc_handler = __malloc_alloc_oom_handler;
		if (my_malloc_handler==0) {__THROW_BAD_ALLOC;}
		my_malloc_handler();
		result = realloc(p, n);
		if (result) return result;
	}
}
typedef __malloc_alloc_template<0> malloc_alloc;


enum {__ALIGN=8};
enum {__MAX_BYTE=128};
enum {__NFREELISTS=__MAX_BYTE / __ALIGN};

// ***二级malloc***
// 二级配置器维护了16个链表(free-list),分别管理8,16,24~128bytes的小额区块(对于需要分配的内存,会向上补全为8的倍数
template <bool threads, int inst>
class __default_alloc_template{
	private:
		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 obj * volatile free_list[__NFREELISTS];
		static size_t FREELIST_INDEX(size_t bytes){
			return (bytes + __ALIGN-1)/(__ALIGN-1);
		}
		static void *refill(size_t);    //无可用free_list时调用,重新填充free_list,配置一块理论上nobjs个大小为传参的空间
		static char *chunk_alloc(size_t size,int &nobjs);  //从内存池获取内存的具体方法
		// chunk_alloc相关的内存池属性
		static char *start_free;
		static char *end_free;
		static size_t heap_size;
	public:
		static void *allocate(size_t n){
			/*大致逻辑就是大于128走一级分配
			 * 如果free_list中存在则取,并且更新链表
			 * 没有的话通过refill重新配置一大块区域
			 * */
			obj * volatile *my_free_list;
			obj *result;
			if (n>size_t(__MAX_BYTE)) {
				return malloc_alloc::allocate(n);
			}
			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->free_list_link;
			return result;
		}
		static void deallocate(void *p, size_t n) {
			obj *q=(obj *)p;
			obj *volatile *my_free_list;
			if (n>(size_t) __MAX_BYTE){
				malloc_alloc::deallocate(p, n);
				return;
			}
			my_free_list = free_list + FREELIST_INDEX(n);
			q->free_list_link = *my_free_list;
			*my_free_list = q;
		}
		static void *reallocate(void *p, size_t old_sz, size_t new_sz);
};
// static的一些初始化
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;

template<bool threads, int inst> 
typename __default_alloc_template<threads, inst>::obj *volatile 
__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;
	obj * volatile *my_free_list;
	obj *result;
	obj *current_obj, *next_obj;
	int i;
	char *chunk = chunk_alloc(n, nobjs); //传的是int引用会更新
	if (1==nobjs) return (chunk); //只有一个的话不需要调整free_list
	my_free_list = free_list + FREELIST_INDEX(n);
	result = (obj *) chunk;
	*my_free_list = next_obj=(obj*) (chunk +n);
	for (i=1;;i++){
		current_obj = next_obj;
		next_obj = (obj*) (chunk +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){
	//一些分之递归调用自己是为了修正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;
		return result;
	} else if (bytes_left >= size) {
		// 不能满足,但至少能提供一个
		nobjs = bytes_left/size;
		total_bytes = nobjs * size;
		result = start_free;
		start_free+=total_bytes;
		return result;
	} else {
		size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size>>4);
		if (bytes_left>0){
			//如果还有可用内存,先分配给free_list上小于其的最大链表维护的空间(可以优化)
			obj * volatile *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){
			//如果heap无可用内存,尝试从更大的free_list上获取内存
			int i;
			obj *volatile *my_free_list, *p;
			for (i=size;i<=__MAX_BYTE;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;
			start_free = (char *) malloc_alloc::allocate(bytes_to_get); //理论上会导致触发set_malloc_handler
		}
		heap_size += bytes_to_get;
		end_free = start_free + bytes_to_get;
		return chunk_alloc(size, nobjs);
	}
}

#endif
复制代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值