1.gnu c++中的allocator類型
- array_allocator
- bitmap_allocator(和mfc中的bitmap無關)
- malloc_allocator
- mt_allocator(多線程)
- new_allocator
- extptr_allocator
- debug_allocator
- pool_allocator
- throw_allocator
其中new_allocator與pool_allocator在STL內存分配allocator介紹過。
class allocator只擁有typedef constructor 和rebind等成員,它繼承自一個high-speed extension allocators。因此所有的分配和釋放都取決於base class,而這個base class也許是終端用戶無法觸碰和操作的。因爲有的是private繼承的。而且主要用於分配內存的函數都在基類中,如pool_allocator的_M_refill。
2.malloc_allocator
// NB: __n is permitted to be 0. The C++ standard says nothing
// about what the return value is when __n == 0.
pointer
allocate(size_type __n, const void* = 0)
{
if (__n > this->max_size())
std::__throw_bad_alloc();
pointer __ret = static_cast<_Tp*>(std::malloc(__n * sizeof(_Tp)));
if (!__ret)
std::__throw_bad_alloc();
return __ret;
}
// __p is not permitted to be a null pointer.
void
deallocate(pointer __p, size_type)
{ std::free(static_cast<void*>(__p)); }
malloc_allocator
的allocate
直接調用的malloc
,deallocate
直接調用free
。
3.array_allocator
_M_array
的類型是array_type*
;array_type* _M_array;
array_type
類型與_Array
相同;typedef _Array array_type;
_Array
是傳入的參數,默認爲std::tr1::array<_Tp, 1>
。
3.1 構造函數
array_allocator(array_type* __array = 0) _GLIBCXX_USE_NOEXCEPT
: _M_array(__array), _M_used(size_type()) { }
_M_array
使用__array
初始化,__array
默認值爲0,用戶如果不寫就是0,如果寫就是用戶傳進來的地址,這個地址在allocate
中有用到。
3.2 使用形式
int my[65536];
array_allocator<int, array<int, 65536>> myalloc(&my);//template<typename _Tp, typename _Array = std::tr1::array<_Tp, 1> >, 前面的_Tp與後面array中的_Tp要一致。
在構造函數中需要傳入一個指針,這個指針可以指向靜態分配的數組,也可以指向動態分配的數組,所以說內存分配不是在array_allocator中進行的,array_allocator只是對分配好的內存進行管理。
3.3 deallocate
array_allocator中沒有寫deallocate,但是他的父類中有,父類中的deallocate如下:
void
deallocate(pointer, size_type)
{
// Does nothing.
}
可以看到,deallocate只是保留了一個接口,但內部什麼也沒有做,因爲內存不是在array_allocator分配的,所以它也不能free。
3.4 array_allocator
pointer
allocate(size_type __n, const void* = 0)
{
if (_M_array == 0 || _M_used + __n > _M_array->size())//如果內存不夠用,拋出異常
std::__throw_bad_alloc();
pointer __ret = _M_array->begin() + _M_used;//得到返回給用戶的指針
_M_used += __n;//調整指針位置
return __ret;
}
可以看出它只是對已經分配好的一塊進行管理,每次用戶要內存就在分好的內存中給用戶一塊,同時將移動當前使用量指針_M_used
。
4.bitmap_allocator
視頻講解連接:bitmap_allocator上
4.1 allocate
pointer
allocate(size_type __n)
{
if (__n > this->max_size())
std::__throw_bad_alloc();
if (__builtin_expect(__n == 1, true))//如果數量爲1個,則調用_M_allocate_single_object()
return this->_M_allocate_single_object();
else //如果申請的數量大於1個,則直接調用operator new
{
const size_type __b = __n * sizeof(value_type);
return reinterpret_cast<pointer>(::operator new(__b));
}
}
4.2 deallocate
void
deallocate(pointer __p, size_type __n) throw()
{
if (__builtin_expect(__p != 0, true))
{
if (__builtin_expect(__n == 1, true))//如果數量爲1,則調用_M_deallocate_single_object()釋放
this->_M_deallocate_single_object(__p);
else
::operator delete(__p);//數量大於1使用operator delete釋放
}
}
4.3 內部結構
4.3.1 分配
首先是64個blocks,每一個對象佔用1個block,64個blocks加上bitmap[1]和bitmap[0]再加上use count形成一個super block。即super block = 64*block + bitmap[1] + bitmap[0];
use count表示64個block已經用了幾個,如果某一個block被使用了,那麼bitmap[1]和bitmap[0]相應bit的數字變爲0,bitmap[1]和bitmap[0]均爲unsigned int,所以兩個加起來共有64比特,也就可以表示64個block的使用情況。
在最前面還有一個數字,表示super block的大小,上圖中的大小爲:4(use count)+4 * 2(bitmap)+64*8(單個對象大小)=524字節。單個對象的大小隻能是8的倍數。
多個super block
是通過__mini_vector
管理的,__mini_vector
是一個vector,但不是標準庫的vector,而是自己寫的一個vector,但它與標準庫中的vector擁有同樣的特性——兩倍增長。也就是說,如果64個block用光了,下次會分配128個block。
__mini_vector
有3個用來管理內存的成員變量。
private:
pointer _M_start;
pointer _M_finish;
pointer _M_end_of_storage;
當64個block用光時,會再申請128個,然後bitmap變爲4個。和vector的成長一樣,會申請另一塊內存然後將現在的搬過去:
當128個用光的時候就會再去分配256個:
每次都這樣成長兩倍,2的冪次方級別地成長。
每個entry代表一種value_type:意思是說,即使兩種類的對象擁有相同的大小,但是由於其value_type不同,不能共用一個super block。
4.3.2 回收
第一個super block全部回收,會有另外一個vector中的元素(free_list)指向回收的區塊,下次再分配super block時,數量會減半,也就是會分配128個。
Q:如果第一個super block沒有被全部回收,還剩2個,後面的super block也還有空餘,那麼下次再分配2個的時候是從第一個super block分配還是從後面的super block分配?
A:從後面的區塊分配。
Q:那後面的被用光了怎麼辦呢?是重新分配還是使用第一個super block空閒的2塊呢?
A:使用super block中空閒的兩個。
不斷地回收,然後管理回收區域的vector就不斷增長,但是它只能包含64個entry,當第65個再被回收時,如果新加入的第65個比前64箇中最大的還要大,那麼直接刪除第65個,不將其添加至free_list;如果加入的第65個比前64箇中最大的小,那麼就將前64箇中最大的刪除,然後將第65個添加至free_list。
Q:假設現在3個super block全部被回收了,也就是上圖中的__S_mem_blocks爲空,而__S_free_list有3個super block,那麼下次再分配的時候是分配第四個super block還是從回收的3箇中拿一個出來?
A:從回收的3箇中拿一個出來。