空間配置器,顧名思義就是為各個容器高效的管理空間(空間的申請與**)的,在默默地工作。雖然在常規使用stl時,可能用不到它,但站在學習研究的角度,學習它的實現原理對我們有很大的幫助。
前面在模擬實現vector、list、map、unordered_map等容器時,所有需要空間的地方都是通過new申請,delete釋放空間的。
new申請空間
1.operator new—>malloc雖然**可以正常執行,但是有以下不足之處:有些構造函式呼叫不是必須的—建構函式意義不大還會影響程式效能2.呼叫析構函式—完成物件的構造
vector和list中都可以存放元素—假設存放10000個元素—>都是乙個乙個的節點,需要使用12個位元組,但實際申請了12+36個位元組的空間,造成額外空間的浪費
頻繁向系統申請小塊記憶體塊malloc,容易造成記憶體碎片
頻繁向系統申請小塊記憶體,影響程式執行效率,效能會下降
空間申請與釋放需要使用者自己管理,容易造成記憶體洩漏
直接使用malloc與new進行申請,每塊空間前有額外空間浪費
申請空間失敗怎麼應對
**結構比較混亂,**復用率不高
未考慮執行緒安全問題
stl設計原則:通用性+高效率
因此需要設計一塊高效的記憶體管理機制
以上提到的幾點不足之處,最主要還是:頻繁向系統申請小塊記憶體造成的。那什麼才算是小塊記憶體?sgi-stl以128作為小塊記憶體與大塊記憶體的分界線,將空間配置器其分為兩級結構,一級空間配置器處理大塊記憶體,二級空間配置器處理小塊記憶體。
頻繁向系統申請一些小的記憶體塊
大於128位元組—>大記憶體塊,一級空間配置器實現原理:malloc+free—>set_new_handle小於等於128位元組—>小記憶體塊,二級空間配置器
以下**來自於stl_alloc.h
為了解決頻繁向系統申請小的記憶體塊造成的缺陷template <
int inst>
class _malloc_alloc_template
// 對free的封裝
static
void
deallocate
(void
*p, size_t /* n */
)// 模擬set_new_handle
// 該函式的引數為函式指標,返回值型別也為函式指標
// void (* set_malloc_handler( void (*f)() ) )()
static
void(*
set_malloc_handler
(void
(*f)()
))()
};// malloc申請空間失敗時代用該函式
template <
int inst>
void
* _malloc_alloc_template::
oom_malloc
(size_t n)
//系統記憶體空間不足進入該函式
// 如果設定,執行使用者提供的空間不足應對措施
(*my_malloc_handler)()
;//呼叫函式指標對應的函式
// 繼續申請空間,可能就會申請成功
result =
malloc
(n);
if(result)
return
(result)
;//申請失敗,迴圈繼續}}
typedef _malloc_alloc_template<
0> malloc_alloc;
二級空間配置器專門負責處理小於128位元組的小塊記憶體。如何才能提公升小塊記憶體的申請與釋放的方式呢?sgistl採用了記憶體池的技術來提高申請空間的速度以及減少額外空間的浪費,採用雜湊桶的方式來提高使用者獲取空間的速度與高效管理。
1.記憶體池技術
記憶體池就是:先申請一塊比較大的記憶體塊已做備用,當需要記憶體時,直接到記憶體池中去去,當池中空間不夠時,再向記憶體中去取,當使用者不用時,直接還回記憶體池即可。避免了頻繁向系統申請小塊記憶體所造成的效率低、記憶體碎片以及額外浪費的問題。
當使用者需要4個位元組時,將_start向後移動4個位元組
當使用者需要8、10個位元組時,將_start向後移動18個位元組
空間申請:res=_start;_start=+n;
使用者用完時會歸還,但直接歸還並不簡單,所以用鍊錶結構—>管理使用者歸還的記憶體塊
當使用者需要再次申請時:最好優先從歸還鍊錶中找到需要的大小記憶體塊,除非找不到才到記憶體池剩餘塊中找,如上圖,假如經過多次從記憶體池剩餘塊中申請記憶體,記憶體池剩餘塊最後只有10個位元組,當這時使用者需要16個位元組記憶體塊時,記憶體池已經給不出來了,因此,最好不要一直申請記憶體池剩餘塊中的記憶體,只要下面歸還的記憶體塊鍊錶中能夠找到就盡量使用歸還的記憶體塊!
大致步驟就是:
現在已經歸還的記憶體塊中找合適塊
找到—>是否需要分割
不需要—>直接分配未找到—>到記憶體池中申請需要—>分割成需要的大小的記憶體塊
缺陷
效率低—>o(n)—>雜湊—>o(1)簡單描述,僅供參考分割多次就會導致記憶體塊越來越小,只能解決外部碎片(空間固定為x的倍數:根據系統選擇)
最少為4的倍數—>32位系統:
最少為8的倍數—>64位系統(會出現浪費過多):128/8
申請歸還比較高效
void
allocate
(size_t n)
void
deallocate
(void
* p, size_t n)
void
*refill
(size_t n/*已經是8的整數倍*/
)void
*chunk_alloc
(size_t size, size_t& nobjs/*20*/
)else
if(leftbytes >= size)
//記憶體池不能提供20塊,至少可以提供一塊
else
//leftbytes
heap_size +
= total_get;
finish = _start + total_get;
return
chunk_alloc
(nobjs,n);}
}
STL 空間配置器
stl有6大元件 容器 演算法 迭代器 仿函式 配接器 分配器。它們之間的密切關係是stl的精髓所在,容器用來存放資料,而容器存在的前提是要有分配器給它分配記憶體,接下來需要實現演算法,迭代器便作為演算法來對容器資料操作的橋梁,演算法可以使用仿函式完成不同的策略變化,配接器可修飾或套接仿函式。說了麼...
STL 空間配置器
stl空間配置器的底層原理 維護了乙個狹義的記憶體池,並且用乙個自由鍊錶來維護該記憶體池。該自由鍊錶比較類似於雜湊的開鏈法的儲存結構。源 pragma once using namespace std define throw bad alloc cerr out of memory endl ex...
STL空間配置器
一級空間配置器 ifndef malloc alloc template h define malloc alloc template h if 0 include define throw bad alloc throw bad alloc elif defined throw bad alloc...