這篇部落格主要講一下sgi-stl中的空間配置器的工作流程。
一、專案背景
小塊記憶體帶來的記憶體碎片問題(外碎片問題)
小塊記憶體頻繁申請釋放帶來的效能問題
二、空間配置器的思想
對於我們來說,對new和delete很熟悉,這兩個函式可以分別完成記憶體的申請和釋放,和c裡面的malloc和free如出一轍。
std::alloc的主要思想是:
(1)定義乙個空間大小閾值,128bytes;
(2)如果申請的空間大於128bytes,那麼就呼叫第一級空間介面卡來完成分配工作;
(3)如果小於128bytes,那麼就呼叫第二級空間介面卡來完成。
一級空間配置器
一級空間配置器是以malloc(),free(),realloc()等c函式執行實際的記憶體配置、釋放、重配置操作,並實現了c++的set_new_handler()函式。
它的內部設計實際就是為了壓榨剩餘的記憶體,達到記憶體的高效運用。所以一級空間配置器內部其實就是malloc和free的封裝,然後盡量的開闢出使用者想要的記憶體空間。
具體步驟如下:
一級空間配置器
二級空間配置器
二級空間配置器使用記憶體池+自由鍊錶的形式避免了小塊記憶體帶來的碎片化,提高了分配的效率,提高了利用率。
見下圖:
二級空間配置器
專案流程
list.h、vector.h是模仿stl容器中的兩個list和vector的介面
allocate.h裡面定義了記憶體池即空間配置器的兩級實現
construct.h裡面主要實現了物件的構造與析構介面
iterator.h裡面有五種迭代器的定義、迭代器萃取、反向迭代器定義、迭代器型別萃取以及兩個非常有用的函式 distance、advance
typetraits.h定義了迭代器所指節點的值型別萃取,是否是pod型別
uninitialized.h裡面是主要是一些拷貝、填充類的函式
main.cpp是測試檔案,用來測試兩個容器的介面和記憶體池實現正確與否
空間配置器的問題
在空間配置器中所有的函式和變數都是靜態的,所以他們在程式結束的時候才會被釋放發。二級空間配置器中沒有將申請的記憶體還給作業系統,只是將他們掛在自由鍊錶上。所以說只有當你的程式結束了之後才會將開闢的記憶體還給作業系統。
由於它沒有將記憶體還給作業系統,所以就會出現二種極端的情況。
2.1. 假如我不斷的開闢小塊記憶體,最後將整個heap上的記憶體都掛在了自由鍊錶上,但是都沒有用這些空間,再想要開闢乙個大塊記憶體的話會開闢失敗。
2.2. 再比如我不斷的開闢char,最後將整個heap記憶體全掛在了自由鍊錶的第乙個結點的後面,這時候我再想開闢乙個16個位元組的記憶體,也會失敗。
解決方法:
我想的是,針對2.1我們可以引入釋放二級空間配置器的方法,但是這個釋放比較麻煩。針對2.2我們可以合併自由鍊錶上的連續的小的記憶體塊。
為什麼引入複雜的兩級空間配置器?
(1). 頻繁使用malloc,free開闢釋放小塊記憶體帶來的效能效率的低下
(2). 記憶體碎片問題,導致不連續記憶體不可用的浪費
為什麼所有成員函式都是靜態的?
是為了在外面通過作用域就可以呼叫,而不需要構造物件
rount_up的實現原理是什麼?
static size_t round_up(size_t bytes)//作用是將非8倍數的整數上調到8的倍數。
round_up實現原理:
以 bytes = 30為例:
a. (30 + 7) = 37, 可以知道30的round_up為32, 給32加(8-1)的目的是讓其(一定)大於32
b. 這樣的話,以十進位制形式,37肯定可以表達為如下的形式:(x*8 + y), 並且可以肯定這個y的值小於8,上述37表達形式為:(32 + 5)
c. 根據上述b步驟的形式,我們很自然的明白, 只要將 ((bytes) + 8-1) 轉換為(x*8 + y)的形式後,然後再將y的值減去就達到目的了
d. 總結上述思路,我們要做事情有兩個,一是將 ((bytes) + 8-1) 轉換為 (x*8 + y)的形式; 二是將(x*8 + y)中的y值清零
e. 在十進位制下,要完成上述任務,比較困難,而在二進位制下,就非常好辦
f. 首先,將 ((bytes) + 8-1) 轉換為(x*8 + y), 只要將 ((bytes) + 8-1)的值轉為二進位制,如以37為例,其二進位制為: 00100101,這樣寫你可能看得還不是很直觀,這樣寫 (00100) (101),這樣就可以發現,第乙個小括號裡面的值就為(x*8), 第二個小括號裡面的值就為(y);接下來,我們的目的就很簡單的, 將第二個小括號裡面的所有位置為0即可, 參考上述網頁中的規則「非運算和與運算結合,可以確保將指定位 清0, 故 & ~(__align - 1)的目的就是將第二個小括號中的位清0。
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...