allocator
)代碼ios
STL全部的對象都存放於容器中,空間的配置由
allocator
負責c++
STL(SGI STL)提供了兩個空間配置器git
std::alloc
默認使用的,作了不少的優化std::allocator
效率不佳,僅僅是對::operator new
和::operator delete
的簡單封裝
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
複製代碼
std::alloc
將new和delete各自的兩步操做分開;能夠減小沒必要要的配置和釋放github
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
複製代碼