詳解STL中的空間配置器(SGI版本)

空間配置器

1.什么是空間配置器

為各個容器高效的管理空間(空間的申請與回收)的

2.為什么需要空間配置器

各種容器----->可以存放元素---->底層需要空間

new 申請空間
  1. operator new ---->malloc
  2. 調用構造函數------完成對象的構造
動態內存管理總結

前面的容器中,每次開辟空間都用的是new,但是用new有一些不好的地方

  • 空間申請與釋放需要用戶自己管理,容易造成內存泄漏
  • 頻繁向系統申請小塊內存塊,容易造成內存碎片例如:結點
  • 頻繁向系統申請小塊內存,影響程序運行效率
  • 直接使用malloc與new進行申請,每塊空間前有額外空間浪費 (malloc會在申請的空間前后添加新的東西)
  • 申請空間失敗怎么處理
  • 代碼結構比較混亂,代碼復用率不高
  • 未考慮線程安全問題

高效的內存管理方式

3.SGI–STL空間配置器如何實現

小塊內存
  1. 大于128字節----->大塊內存
  2. 小于等于128字節----->小塊內存
  • 一級空間配置器處理大塊內存,
  • 二級空間配置器處理小塊內存。

一級空間配置器

malloc+free----->set_new_handle

_malloc_alloc_template:

申請空間
void * allocate(size_t n)
{// 申請空間成功,直接返回,失敗交由oom_malloc處理void * result =malloc(n);if(nullptr == result)result = oom_malloc(n);return result;
}
釋放空間
static void deallocate(void *p, size_t /* n */)
{ free(p);
}
malloc失敗后的處理oom_malloc
  1. 接受函數指針(調用set_new_handle)
  2. 驗證該函數指針是否為空
    • 是:直接拋異常
  3. 調用該函數指針對應的函數
  4. 調用malloc繼續申請空間
    • 申請成功:直接返回
    • 申請失敗:循環繼續
template <int inst>
void * __malloc_alloc_template<inst>::oom_malloc(size_t n)
{void (* my_malloc_handler)();void *result;for (;;){// 檢測用戶是否設置空間不足應對措施,如果沒有設置,拋異常,模式new的方式my_malloc_handler = __malloc_alloc_oom_handler;if (0 == my_malloc_handler){__THROW_BAD_ALLOC;}// 如果設置,執行用戶提供的空間不足應對措施(*my_malloc_handler)();// 繼續申請空間,可能就會申請成功result = malloc(n);if (result)return(result);}
}
set_new_handle

返回值類型以及參數類型都是void*()函數指針

// 該函數的參數為函數指針,返回值類型也為函數指針
// void (* set_malloc_handler( void (*f)() ) )()
static void (* set_malloc_handler(void (*f)()))()
{void (* old)() = __malloc_alloc_oom_handler;__malloc_alloc_oom_handler = f;return(old);
}

二級空間配置器

頻繁向系統申請小的內存塊造成的缺陷
SGI—STL采用了內存池的技術來提高申請空間的速度以及減少額外空間的浪費采用哈希桶的方式來提高用戶獲取空間的速度與高效管理

內存池

內存池就是:先申請一塊比較大的內存塊已做備用,當需要內存時,直接到內存池中去去,當池中空間不夠時,再向內存中去取,當用戶不用時,直接還回內存池即可。避免了頻繁向系統申請小塊內存所造成的效率低、內存碎片以及額外浪費的問題

char* _start,*finish

在這里插入圖片描述

申請空間
  1. 現在已經歸還的內存塊中找合適的塊
  2. 找到---->是否需要分割---->不需要---->直接分配
    需要----->再對內存塊進行分割
  3. 未找到----->去內存池中申請
申請空間的缺陷
  1. 鏈表查找合適內存塊,需要遍歷,效率低
  2. 分割
注意問題
  1. 當用戶需要空間時,能否直接從內存池中大塊空間中直接截取?為什么?
    答:優先從鏈表中選,先從大塊中拿,如果用戶需要大塊的空間,可能就給不了了
  2. 對用戶歸還的空間能否直接拼接在大塊內存前?
    答:不行
  3. 對用戶歸還的空間如何進行管理?
    答:用鏈表連接起來
  4. 不斷切割會有什么后果?
    答:內存碎片

二級空間配置器的設計

SGI-STL中的二級空間配置器使用了內存池技術,但沒有采用鏈表的方式對用戶已經歸還的空間進行管理(因為用戶申請空間時在查找合適的小塊內存時效率比較低),而是采用了哈希桶的方式進行管理效率更高。那是否需要128桶個空間來管理用戶已經歸還的內存塊呢?答案是不需要,因為用戶申請的空間基本都是4的整數倍,其他大小的空間幾乎很少用到。因此:SGI-STL將用戶申請的內存塊向上對齊到了8的整數倍

32位系統用4的倍數
64位系統用8的倍數

每個鏈表中肯定必須有一個指針,32位指針是4個字節,64位下是8個字節
在這里插入圖片描述

template <int inst>
class __default_alloc_template
{
private:enum { __ALIGN = 8 }; // 如果用戶所需內存不是8的整數倍,向上對齊到8的整數倍enum { __MAX_BYTES = 128 }; // 大小內存塊的分界線enum { __NFREELISTS = __MAX_BYTES / __ALIGN }; // 采用哈希桶保存小塊內存時所需桶的個數// 如果用戶所需內存塊不是8的整數倍,向上對齊到8的整數倍static size_t ROUND_UP(size_t bytes){return (((bytes)+__ALIGN - 1) & ~(__ALIGN - 1));}
private:// 用聯合體來維護鏈表結構----同學們可以思考下此處為什么沒有使用結構體union obj{union obj * free_list_link;char client_data[1]; /* The client sees this. */};
private:static obj * free_list[__NFREELISTS];// 哈希函數,根據用戶提供字節數找到對應的桶號static size_t FREELIST_INDEX(size_t bytes){return (((bytes)+__ALIGN - 1) / __ALIGN - 1);}// start_free與end_free用來標記內存池中大塊內存的起始與末尾位置static char *start_free;static char *end_free;// 用來記錄該空間配置器已經想系統索要了多少的內存塊static size_t heap_size;// ...跨平臺操作,封裝鎖,申請空間方式等
};

二級空間配置器的管理空間

void allocate(size_t n)
{
if(n>128)
; //調用一級空間配置器
1. 用n計算桶號
2. 檢測該桶中是否有結點(內存塊)
有:使用頭刪法將第一個內存塊返回
沒有:return refill(Round_up(n));
}

void refill(size_t/已經是8的整數倍/)
{
size_t nobjs =20;
char* chunk = chunk_alloc(n,nobjs);
if(nobjs == 1) //只要了一塊
return chunk;
//1<nobjs<=20
將第一個內存保存,最后要返回給外部用戶
將剩余nobjs-1個內存塊掛接到對應的桶中
}

void * chunk_alloc(size_t size,size_t& nobjs//20)
{
size_t totalBytes = nobjs*size;
size_t leftBytes = finish-start;

void * res =null;
if(leftBytes > = totalBytes) //內存池空間足以提供20
{res = start;start+=totalBytes;return res;}
else if(leftBytes>=size)//不夠20至少提供1塊
{nobjs =leftBytes/size;res=start;start+=nobjssize;return res;}//實際能提供多少塊
else //內存池的空間也不足,連一塊也提供不了
{
//1.將內存池中剩余的內存—>掛接到相應桶中
//total_get = 2
total_bytes+RoundUP(heap_size>>4);
}
}

  1. 申請空間
    在這里插入圖片描述
// 函數功能:向空間配置器索要空間
// 參數n: 用戶所需空間字節數
// 返回值:返回空間的首地址
static void * allocate(size_t n)
{obj * __VOLATILE * my_free_list;obj * __RESTRICT result;// 檢測用戶所需空間釋放超過128(即是否為小塊內存)if (n > (size_t)__MAX_BYTES){// 不是小塊內存交由一級空間配置器處理return (malloc_alloc::allocate(n));}// 根據用戶所需字節找到對應的桶號my_free_list = free_list + FREELIST_INDEX(n);result = *my_free_list;// 如果該桶中沒有內存塊時,向該桶中補充空間if (result == 0){// 將n向上對齊到8的整數被,保證向桶中補充內存塊時,內存塊一定是8的整數倍void *r = refill(ROUND_UP(n));return r;}// 維護桶中剩余內存塊的鏈式關系*my_free_list = result->free_list_link;return (result);
};
  1. 回收空間
    在這里插入圖片描述
    二級空間配置器中沒有釋放空間
// 函數功能:用戶將空間歸還給空間配置器
// 參數:p空間首地址 n空間總大小
static void deallocate(void *p, size_t n)
{obj *q = (obj *)p;obj ** my_free_list;// 如果空間不是小塊內存,交給一級空間配置器回收if (n > (size_t)__MAX_BYTES)	//超過128,按照一級空間配置進行釋放{malloc_alloc::deallocate(p, n);return;}//沒到128// 找到對應的哈希桶,將內存掛在對應哈希桶中my_free_list = free_list + FREELIST_INDEX(n);q->free_list_link = *my_free_list;*my_free_list = q;
}
  1. 向內存中補充空間

在這里插入圖片描述

template <int inst>
char* __default_alloc_template<inst>::chunk_alloc(size_t size, int& nobjs)
{// 計算nobjs個size字節內存塊的總大小以及內存池中剩余空間總大小char * result;size_t total_bytes = size * nobjs;size_t bytes_left = end_free - start_free;// 如果內存池可以提供total_bytes字節,返回if (bytes_left >= total_bytes){result = start_free;start_free += total_bytes;return(result);}else if (bytes_left >= size){// nobjs塊無法提供,但是至少可以提供1塊size字節內存塊,提供后返回nobjs = bytes_left / size;total_bytes = size * nobjs;result = start_free;start_free += total_bytes;return(result);}else{// 內存池空間不足,連一塊小塊村內都不能提供// 向系統堆求助,往內存池中補充空間// 計算向內存中補充空間大小:本次空間總大小兩倍 + 向系統申請總大小/16size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);// 如果內存池有剩余空間(該空間一定是8的整數倍),將該空間掛到對應哈希桶中if (bytes_left > 0){// 找對用哈希桶,將剩余空間掛在其上obj ** my_free_list = free_list + FREELIST_INDEX(bytes_left);((obj *)start_free)->free_list_link = *my_free_list;*my_ree_list = (obj *)start_free;}// 通過系統堆向內存池補充空間,如果補充成功,遞歸繼續分配start_free = (char *)malloc(bytes_to_get);if (0 == start_free){// 通過系統堆補充空間失敗,在哈希桶中找是否有沒有使用的較大的內存塊int i;obj ** my_free_list, *p;for (i = size; i <= __MAX_BYTES; 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置空,因為一級空間配置器一旦拋異常就會出問題end_free = 0;//end_free作用是標記內存池空間的末尾start_free = (char *)malloc_alloc::allocate(bytes_to_get);}// 通過系統堆向內存池補充空間成功,更新信息并繼續分配heap_size += bytes_to_get;end_free = start_free + bytes_to_get;return(chunk_alloc(size, nobjs));}
}

空間配置器的默認選擇

SGI-STL默認使用一級還是二級空間配置器,通過USE_MALLOC宏進行控制:

#ifdef __USE_MALLOCtypedef malloc_alloc alloc;typedef malloc_alloc single_client_alloc;
#else// 二級空間配置器定義
#endif

在SGI_STL中該宏沒有定義,因此:默認情況下SGI_STL使用二級空間配置器

空間配置器的再次封裝

在C++中,用戶所需空間可能是任意類型的,有單個對象空間,有連續空間,每次讓用戶自己計算所需空間總大小不是很友好,因此SGI-STL將空間配置器重新再封裝了一層:

template<class T, class Alloc>
class simple_alloc
{
public:// 申請n個T類型對象大小的空間static T *allocate(size_t n){return 0 == n ? 0 : (T*)Alloc::allocate(n * sizeof (T));}// 申請一個T類型對象大小的空間static T *allocate(void){return (T*)Alloc::allocate(sizeof (T));}// 釋放n個T類型對象大小的空間static void deallocate(T *p, size_t n){if (0 != n)Alloc::deallocate(p, n * sizeof (T));}// 釋放一個T類型對象大小的空間static void deallocate(T *p){Alloc::deallocate(p, sizeof (T));}
};

對象構造與釋放

一切為了效率考慮,SGI-STL決定將空間申請釋放和對象的構造析構兩個過程分離開,因為有些對象的構造不需要調用析構函數,銷毀時不需要調用析構函數,將該過程分離開可以提高程序的性能:

// 歸還空間時,先先調用該函數將對象中資源清理掉
template <class T>
inline void destroy(T* pointer)
{pointer->~T();
}
// 空間申請好后調用該函數:利用placement-new完成對象的構造
template <class T1, class T2>
inline void construct(T1* p, const T2& value)
{new (p)T1(value);
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/383090.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/383090.shtml
英文地址,請注明出處:http://en.pswp.cn/news/383090.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【劍指offer】_15數組中重復的數字

題目描述 在一個長度為n的數組里的所有數字都在0到n-1的范圍內。 數組中某些數字是重復的&#xff0c;但不知道有幾個數字是重復的。也不知道每個數字重復幾次。請找出數組中任意一個重復的數字。 例如&#xff0c;如果輸入長度為7的數組{2,3,1,0,2,5,3}&#xff0c;那么對應的…

【劍指offer】_16 構建乘積數組

題目描述 給定一個數組A[0,1,…,n-1],請構建一個數組B[0,1,…,n-1],其中B中的元素B[i]A[0]*A[1]*...*A[i-1]*A[i1]*...*A[n-1]。不能使用除法。 解題思路 設有數組大小為5。 對于第一個for循環 第一步&#xff1a;b[0] 1; 第二步&#xff1a;b[1] b[0] * a[0] a[0] 第三…

【劍指offer】_17正則表達式的匹配

題目描述 請實現一個函數用來匹配包括’.‘和*的正則表達式。模式中的字符’.表示任意一個字符&#xff0c;而*表示它前面的字符可以出現任意次&#xff08;包含0次&#xff09;。 在本題中&#xff0c;匹配是指字符串的所有字符匹配整個模式。例如&#xff0c;字符串"aaa…

大四階段的社會實踐的主要目的是_疫情當前,大三大四的學生“很慘”?大一大二的學生也別松懈...

大四畢業生不容易這次疫情對于高校學生而言&#xff0c;可以說是各有各的難處&#xff0c;“這屆畢業生很慘”更是屢上熱搜。不可否認&#xff0c;大四畢業生確實很不容易&#xff0c;論文答辯、畢業、求職就業等都受到了影響&#xff0c;雖然有困難&#xff0c;但各方都在積極…

【劍指offer】_18 數據流中的中位數

題目描述 如何得到一個數據流中的中位數&#xff1f;如果從數據流中讀出奇數個數值&#xff0c;那么中位數就是所有數值排序之后位于中間的數值。如果從數據流中讀出偶數個數值&#xff0c;那么中位數就是所有數值排序之后中間兩個數的平均值。我們使用Insert()方法讀取數據流…

【劍指offer】_19 滑動窗口中的最大值

題目描述 給定一個數組和滑動窗口的大小&#xff0c;找出所有滑動窗口里數值的最大值。例如&#xff0c;如果輸入數組{2,3,4,2,6,2,5,1}及滑動窗口的大小3&#xff0c;那么一共存在6個滑動窗口&#xff0c;他們的最大值分別為{4,4,6,6,6,5}&#xff1b; 針對數組{2,3,4,2,6,2,…

android 文字反轉_多文字共享信息系統

歐陽貴林 www.HeZi.net首發表于2016年03月23日“ 處在信息時代的開端&#xff0c;信息技術不應有特殊的文字性&#xff0c;需要創建多文字共享信息系統&#xff0c;給各國文字一個公平的參與信息與科技創新發展的平臺。這是世界的事&#xff0c;更是中國事。”01人類語言語言文…

LeetCode【1--兩數之和】 LeetCode【2--兩數相加】

兩數之和 題目描述 給定一個整數數組 nums 和一個目標值 target&#xff0c;請你在該數組中找出和為目標值的那 兩個 整數&#xff0c;并返回他們的數組下標。 你可以假設每種輸入只會對應一個答案。但是&#xff0c;你不能重復利用這個數組中同樣的元素。 解題思路 直接兩…

input數字開頭不能為0_李商隱為初戀寫詩,每句以數字開頭,最后10字一直被仿從未被超越...

上學時&#xff0c;每次寫作文&#xff0c;老師總愛在耳邊念叨&#xff1a;“你的作文得讓閱卷老師看得懂&#xff0c;不然不可能給你高分的&#xff01;”每次聽到話&#xff0c;筆者總是用李商隱的詩來和他斗嘴。是的&#xff0c;李商隱的詩作常常是讓人讀不懂的&#xff0c;…

lsass.exe 當試圖更新密碼時_“驅動人生”下載器木馬再度更新-你應該注意什么?...

360安全大腦監測到通過"驅動人生"供應鏈攻擊傳播的挖礦木馬在1月30日下午4時左右再次更新。此次更新中&#xff0c;木馬在此前抓取系統帳戶密碼的基礎上增加了抓取密碼hash值的功能&#xff0c;并試圖通過pass the hash攻擊進行橫向滲透&#xff0c;使得該木馬的傳播…

LeetCode【3--無重復的最長字串】 LeetCode【4--有序數組中的中位數】

無重復的最長字串 題目描述 給定一個字符串&#xff0c;請你找出其中不含有重復字符的 最長子串 的長度。 解題思路 看到這道題&#xff0c;其實就兩個步驟&#xff0c;遍歷字符串&#xff0c;記錄當前字符有沒有重復。 重復一般解決就是哈希&#xff0c;這里用個數組表示…

hwt字體轉換ttf_五分鐘教你弄懂了字體反爬是個啥

今天的文章內容主要是關于字體反爬。目前已知的幾個字體反爬的網站是貓眼&#xff0c;汽車之家&#xff0c;天眼查&#xff0c;起點中文網等等。以前也看過這方面的文章&#xff0c;今天跟個老哥在交流的時候&#xff0c;終于實操了一把&#xff0c;弄懂了字體反爬是個啥玩意。…

LeetCode【5--最長的回文子串】 LeetCode【6--Z字形變換】

最長的回文子串 題目描述 給定一個字符串 s&#xff0c;找到 s 中最長的回文子串。你可以假設 s 的最大長度為 1000。 解題思路 可以跟無重復的最長子串一樣&#xff0c;用一個滑動窗口&#xff0c;只不過這個窗口的右邊界往右&#xff0c;左邊界每回要從右邊界的下標往左…

androidstudio 日歷視圖怎么顯示農歷_中秋國慶旅游攻略怎么做?用這個便簽軟件很簡單...

九月已經到來&#xff0c;中秋節和國慶節距離我們也不遠了&#xff0c;今年的中秋和國慶節重疊了有足足八天的假期。不少人都想趁著這個小長假出門旅游&#xff0c;要想保證旅游質量&#xff0c;那么就要做好攻略。中秋國慶旅游攻略怎么做&#xff1f;要想做好一份中秋國慶旅游…

c++ select函數_PySpark 操作函數一覽

PySpark 操作函數一覽Created: Sep 14, 2020 10:28 AM Tags: Big Data, PySpark, Python, SparkPyspark.sql.functionsfrom pyspark.sql import functions as F函數使用說明基本數學函數類abssin、cos、tan、asin、acos 、atan、sinh、cosh、tanhceil、round、floorexp、log、l…

LeetCode【7--整數反轉】 LeetCode【8--字符串轉整數】

整數反轉 題目描述 給出一個 32 位的有符號整數&#xff0c;你需要將這個整數中每位上的數字進行反轉。 解題思路 x%10 取一位&#xff0c;x/10下一位&#xff0c;注意越界&#xff0c; 代碼實現 class Solution { public:int reverse(int x) {int sum 0;while(x){if(s…

word2003如何設置護眼模式_ERP系統上線,如何設置采購收貨的模式,提升企業的采購效率...

如何合理的規劃采購計劃上次去拜訪一個朋友&#xff0c;他們說公司既然出現沒有下達采購訂單&#xff0c;供應商也有送貨過來的事情&#xff0c;對于公司來說&#xff0c;這個是非常嚴重的問題。若用了ERP系統之后&#xff0c;如何避免類似的事情發生&#xff0c;今天我們來分享…

LeetCode【9-- 回文數】LeetCode【10 --正則表達式的匹配】

回文數 題目描述 判斷一個整數是否是回文數。回文數是指正序&#xff08;從左向右&#xff09;和倒序&#xff08;從右向左&#xff09;讀都是一樣的整數。 解題思路 判斷該數的逆序數是不是和原數相同 代碼實現 class Solution { public:bool isPalindrome(int x) {if(…

sun鍵盤沒有stop鍵_請教Sun鍵盤

請教Sun鍵盤(2011-12-24 06:01:11)標簽&#xff1a;計算機雜談請教Sun鍵盤Sun鍵盤上,Help和F1之間的空白鍵是干啥的?Space旁邊的兩個菱形標志的呢?Compose呢?謝謝!請教Sun鍵盤Space旁邊的兩個菱形標志是一個類似Ctrl、Alt的修飾鍵&#xff0c;叫Meta。可以用Meta;鍵名來定義…

LeetCode【11--盛水最多的容器】LeetCode【12 -- 整數轉羅馬數字】

盛水最多的容器 題目描述 給定 n 個非負整數 a1&#xff0c;a2&#xff0c;…&#xff0c;an&#xff0c;每個數代表坐標中的一個點 (i, ai) 。在坐標內畫 n 條垂直線&#xff0c;垂直線 i 的兩個端點分別為 (i, ai) 和 (i, 0)。找出其中的兩條線&#xff0c;使得它們與 x 軸共…