STL 源碼剖析 空間配置器

  • 以STL的運用角度而言,空間配置器是最不需要介紹的東西,它總是隱藏在一切組件(更具體地說是指容器,container) 的背后
  • 但是STL的操作對象都存放在容器的內部,容器離不開內存空間的分配
  • 為什么不說allocator是內存配置器而說它是空間配置器呢?因為空間不一定 是內存,空間也可以是磁盤或其它輔助存儲介質。是的,你可以寫一個allocator, 直接向硬盤取空間。以下介紹的是SGI STL提供的配置器,配置的對象,這里分配的空間是指分配內存

?

  • ?set_new_handler()總結
namespace JJ
{template <class T>inline T* _allocate(std::ptrdiff_t size,T*){std::set_new_handler(0);T* tmp = (T*)(::operator new ((std::size_t)(size * sizeof(T))));if (tmp == 0){std::cerr << "out of memory " << std::endl;exit(1);}return tmp;}template <class T>inline void _deallocate(T* buffer){::operator delete (buffer);}template <class T1,class T2>inline void _construct(T1* p,const T2& value){new(p) T1(value);}template <class T>inline void _destroy(T* ptr){ptr->~T();}template <class 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;//rebind allocator of type Utemplate <class U>struct rebind{typedef allocator<U> other;};//hint used for locality. ref.[Austern],pl89pointer allocate(size_type n,const void* hint = 0){return _allocate((difference_type)n,(pointer)0);}void deallocate(pointer p,size_type n){_deallocate(p);}void construct(pointer p,const T& value){_construct(p,value);}void destroy(pointer p){_destroy(p);}pointer address(reference x){return (pointer)&x;}const_pointer const_address(const_reference x){return (const_pointer)&x;}size_type max_size()const{return size_type (UINT_MAX / sizeof(T));}};
}
  • SGI STL在這個項目上根本就逸脫了 STL標準規格,使用一個專屬的、擁有次層配置(sub-allocation)能力的、效率優越的特殊配置器,稍后有詳細介紹
  • ?備 次 配 置 力 (sub-allocation)的 S G I 空間配置器
  • SGI STL的配置器與眾不同,也與標準規范不同,其名稱是a llo c 而非 allo ca to r,而且不接受任何參數。換句話說,如果你要在程序中明白采用SGI配 置器,則不能采用標準寫法:

  • ?SGI STL allocator未能符合標準規格,這個事實通常不會給我們帶來困擾,因 為通常我們使用缺省的空間配置器,很少需要自行指定配置器名稱,而SGI STL的每一個容器都已經指定其缺省的空間配置器為a llo c .例如下面的vector聲明:

  • ?S G I標 準 的 空 間 配 量 器 ,std::allocator
  • SG I特 殊 的 空 間 配 置 器 ,std::alloc
  • 上一節所說的a llo c a to r 只是基層內存配置/釋放行為(也就是 ::operator new和 : :operator delete)的一層薄薄的包裝,并沒有考慮到任何效率上的強 化. S G I另有法寶供其本身內部使用。

  • ?這其中的new算式內含兩階段操作3: (1 )調 用 ::operator new配置內存; ⑵ 調 用Foo::Foo()構造對象內容。delete算式也內含兩階段操作:(1)調用Foo: :-Foo ()將對象析構;(2 ) 調 用 ::operator delete釋放內存。
  • 為了精密分工,STL a llo c a to r決定將這兩階段操作區分開來。內存配置操作 由 alloc: al locate ()負責,內存釋放操作由alloc : : deallocate ()負責;對象構造操作由::construct:()負責,對象析構操作由:;destroy負責

  • ?內存空間的配置/釋放與對象內容的構造/析構,分別著落在這兩個文件身上。其 中 <stl_construct .h>定義有兩個基本函數:構造用的 construct() 和析構用的destroy。。在一頭栽進復雜的內存動態配置與釋放之前,讓我們先看清楚這 兩個函數如何完成對象的構造和析構。

  • ?構造和析構基本工具:co n stru ct()和 destroy()

  • ?這兩個作為構造、析構之用的函數被設計為全局函數,符合STL的規范.此外,STL還規定配置器必須擁有名為construct ()和 destroy ()的兩個成員函數(見2.1節 ),然而真正在SGI STL中大顯身手的那個名為std::alloc的配 置器并未遵守這一規則(稍后可見
  • 上 述 construct ()接受一個指針p 和一個初值value,該函數的用途就是 將初值設定到指針所指的空間上。C++的 placement new 運算子5 可用來完成這一任務。
  • destroy() 有兩個版本,第一版本接受一個指針,準備將該指針所指之物析 構掉。這很簡單,直接調用該對象的析構函數即可。第二版本接受first和 last 兩個迭代器(所謂迭代器,第3 章有詳細介紹),準備將 [first, last)范圍內的所有對象析構掉。我們不知道這個范圍有多大,萬一很大,而每個對象的析構函數都無關痛癢(所謂rrzvia/destructor), 那么一次次調用這些無關痛癢的析構函數, 對效率是一種傷害。
  • 因此,這里首先利用value_type()獲得迭代器所指對象的 型別,再利用 _type_traits< T> 判斷該型別的析構函數是否無關痛癢.若是 (一true_type), 則什么也不做就結束;若 否 (— false_type), 這才以循環 方式巡訪整個范圍, 并在循環中每經歷一個對象就調用第一個版本的destroy ()

空 間 的 配 置 與 釋 放 , std::alloc

  • 對象構造前的空間配置和對象析構后的空間釋放,由 <stl_alloc.h>負責, S G I 對此的設計哲學如下:

  • ?C + + 的 內 存 配 置 基 本 操 作 是 ::operator new ( ) , 內存釋放基本操作 是 : operator delete)). 這兩個全局函數相當于C 的malloc ( ) 和 f r e e O 函 數。是的,正是如此,S G I 正是以malloc ()和 f r e e O 完成內存的配置與釋放。?
  • 考慮到小型區塊所可能造成的內存破碎問題,S G I 設計了雙層級配置器,第一級配置器直接使用 malloc() 和 fme(),第二級配置器則視情況采用不同的策略:當配置區塊超過128 bytes時,視 之 為 “足夠大”,便調用第一級配置器;當配 置區塊小于128 bytes時,視 之 為 “過小”,為了降低額外負擔(overhead,見 2.2.6 節 ),便采用復雜的memory pool整理方式,而不再求助于第一級配置器, 整個設 計究竟只開放第一級配置器,或是同時開放第二級配置器,取決于_ use_malloc是否被定義(唔,我們可以輕易測試出來,SGI STL并未定義一_USE_MALLOC)

  • ?無論alloc被定義為第一級或第二級配置器,SGI還為它再包裝一個接口如 下,使配置器的接口能夠符合STL規格:

  • ?其內部四個成員函數其實都是單純的轉調用,調用傳遞給配置器(可能是第一級也可能是第二級)的成員函數.這個接口使配置器的配置單位從bytes轉為個別元素的大小(sizeof (T) ) . SGI STL容器全都使用這個simple_alloc接口,例如:

?第 一 級 配 置 器 ―malloc_alloc_template 剖析

  • ?第一級配置器以malloc () , free () , realloc ()等 C 函數執行實際的內存配置、釋放、重配置操作,并實現出類似C + + new-hand宜了的機制。是的,它不能 直 接 運 用 C++ new-handier機制,因為它并非使用::operator n e w 來配置內存。?
  • 所 謂 C++ new handler機制是,你可以要求系統在內存配置需求無法被滿足 時 ,調用一個你所指定的函數。換句話說,一 旦 ::operator new無法完成任務, 在 丟 出 std::bad_alloc異常狀態之前,會先調用由客端指定的處理例程。該處理例程通常即被稱為new-handiero new-handier解決內存不足的做法有特定的模式,請 參 考 《婀 伽 e C++》2 e 條款7?
  • handler

  • ?程通常即被稱為new-handiero new-handier解決內存不足的做法有特定的模式
  • 注意,S G I 以 malloc而 非 ::operator new來配置內存(我所能夠想象的 一個原因是歷史因素,另一個原因是C + + 并未提供相應于 realloc () 的內存配 置 操 作 ),因此,S G I 不能直接使用C + + 的 set_new_handler (),必須仿真一個類似的 set_malloc_handler ()?
  • 請 注 意 ,S G I 第 一 級 配 置 器 的 allocate ()和 realloc ( ) 都是在調用 malloc ()和 realloc ()不成功后,改調用 oom_malloc ()和 oom_realloc ()=后兩者都有內循環,不 斷 調 用 “內存不足處理例程”,期望在某次調用之后,獲得足夠的內存而圓滿完成任務。但 如 果 “內存不足處理例程”并未被客端設定,oom_malloc() 和 oom_realloc() 便老實不客氣地調用 — THROW_BAD_ALLOC,丟 出 bad_alloc異常信息,或 利 用 exit(l)硬生生中止程序。?
  • 記住,設 計 “內存不足處理例程”是客端的責任,設 定 “內存不足處理例程”也是客端的責任.再一次提醒你,“內存不足處理例程”解決問題的做法有著特定的模式,請 參 考 [Meyers98]條款7

第二級配置器 __default_alloc_template 剖析

  • 第二級配置器多了一些機制,避免太多小額區塊造成內存的碎片。小額區塊帶來的其實不僅是內存碎片,配置時的額外負擔(overhead)也是一個大問題% 額外 負擔永遠無法避免,畢竟系統要靠這多出來的空間來管理內存,如圖2-3所示。但是區塊愈小,額外負擔所占的比例就愈大,愈顯得浪費。

  • SGI第二級配置器的做法是,如果區塊夠大,超過128 bytes時,就移交第一 級配置器處理.當區塊小于128 bytes時,則以內存池(memory pool)管理,此法 又稱為次層配置(sub-allocation):
  • 每次配置一大塊內存,并維護對應之自由鏈表 {free-list). 下次若再有相同大小的內存需求,就直接從free-lists中撥出。如果客戶端釋還小額區塊,就由配置器回收到free-lists中—— 是的,別忘了,配置器除了負 責配置,也負責回收。
  • 為了方便管理,SGI第二級配置器會主動將任何小額區塊的內存需求量上調至8 的倍數(例如客端要求30 bytes,就自動調整為32 bytes),并維護 16 個free-lists,各自管理大小分別為 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128 bytes的小額區塊。free-lists的節點結構如下:

  • 諸君或許會想,為了維護鏈表(lists), 每個節點需要額外的指針(指向下一 個節點),這不又造成另一種額外負擔嗎?你的顧慮是對的,但早已有好的解決辦法。
  • 注意’上 述 obj所用的是union,由于union之故,從其第一字段觀之, obj可被視為一個指針,指向相同形式的另一個。切從其第二字段觀之。obj可 被視為一個指針,指向實際區塊,如圖2-4所示。一物二用的結果是,不會為了維護鏈表所必須的指針而造成內存的另一種浪費(我們正在努力節省內存的開銷呢)。這種技巧在強型(strongly typed)語言如Java中行不通,但是在非強型語言如C++中十分普遍

?空 間 配 置 函 數 allocate()

  • 此函數首先判斷區塊大小,大 于 128 bytes就調用第一級配置器,小 于 128 bytes就檢查對應的free listo 如 果 free list之內有可用的區塊,就直接拿來 用,如果沒有可用區塊,就將區塊大小上調至8 倍數邊界,然后調用refilio, 準備為free list重新填充空間。refill ()將于稍后介紹。

?

  • ?free_list維護8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128 bytes的小額區塊,需要分配內存的時候,如上圖所示,需要分配的大小是96,my_free_list先找到96對應的鏈條,result指向想要的96區塊,my_free_list移動到下一個區塊,將result需要的區塊排除到鏈外,表示為其分配了區間

空 間 釋 放 函 數 deallocate。

  • 身為一個配置器, _ default_alloc_template 擁有配置器標準接口函數deallocated o 該函數首先判斷區塊大小,大于128 bytes就調用第一級配置器, 小于128 bytes就找出對應的free list,將區塊回收。

  • ?使用q指向需要回收的空間,使用my_free_list找到與之大小相匹配的存儲區塊的鏈條,使用q銜接對應的?存儲區塊的鏈條位置,移動初始位置

重 新 填 充 free lists

  • 回頭討論先前說過的allocate (). 當它發現free list中沒有可用區塊了時, 就 調 用 refillO,準備為free list重新填充空間.新的空間將取自內存池(經由 chunk_alloC ()完 成 )。缺省取得2 0 個新節點(新區塊),但萬一內存池空間不 足,獲得的節點數(區塊數)可能小于20:

?內 存 池 ( m e m o r y pool )

  • 從內存池中取空間給斤e。 使用,是 chunk_alloc()的工作:?

  • 上圖存在一個錯誤,size是需要的大小,nobjs是需要的數量

?

  • ?上述的chunk_alloc ( ) 函數以end_free - s t a r t _ f r e e 來判斷內存池的水量。如果水量充足,就直接調出20個區塊返回給free list。如果水量不足以提供20 個區塊,但還足夠供應一個以上的區塊,就撥出這不足20個區塊的空間出去。這時候其pass by reference的n o b js參數將被修改為實際能夠供應的區塊數。如果 內存池連一個區塊空間都無法供應,對客端顯然無法交待,此時便需利用m alloc?從 heap中配置內存,為內存池注入活水源頭以應付需求。新水量的大小為需求量 的兩倍,再加上一個隨著配置次數增加而愈來愈大的附加量。

  • ?如果一開始free_list 和 內存池都是空的,當用戶需求數據的時候,發現沒有內存,申請的空間是20的兩倍,也就是40,一半內存交給free_list用于數據的維護,一半數據給 內存池;當再次申請內存時,所對應的free_list不存在數據,就會先向內存池索要內存,滿足一部分之后,將剩余的內存交給 free_list;

?內存基本處理工具

  • STL定義有五個全局函數,作用于未初始化空間上? 這樣的功能對于容器的實現很有幫助,我們會在第4章容器實現代碼中,看到它們肩負的重任?前兩個函數是2.2.3節說過的、用于構造的construct ()和用于析構的destroy (),另三個函數uninitialized_copy(),uninitialized_fill(),uninitialized_fill_n(), 分別對應于高層次函數copy () fill () fill_n() 這些都是STL算法,將在第6 章介紹。如果你要使用本節的三個低層次函數,應該包含 <memory>, 不過SG I把它們實際定義于 <stl_uninitialized>。

uninitialized_copy

  • ?uninitialized_copy ()使我們能夠將內存的配置與對象的構造行為分離開來。如果作為輸出目的地的[result/ result+(last-first))范圍內的每一個迭代器都指向未初始化區域,則 uninitialized_copy ()會使用copy constructor,給身為輸入來源之[first, last)范圍內的每一個對象產生一份復制品’放進輸出 范圍中。換句話說,針對輸入范圍內的每一個迭代器該函數會調用
    construct (&* (result+ (i-f irst) ) , *i), 產 生 * i 的復制品,放置于輸出范圍的相對位置上。式中的construct ()已于2.2.3節討論過。
  • result 指向內存拷貝的目的地址,i和first指向的是同一個內存區間,使用i和first之間的差值,將*i(元素的數值)拷貝到指定的位置
  • 果你需要實現一個容器,uninitialized.copy() 這樣的函數會為你帶來 很大的幫助,因為容器的全區間構造函數(range constructor) 通常以兩個步驟完 成:

  • ?C++ 標準規格書要求 uninitialized_copy () 具 有 'commit or rollback 語 意,意思是要么“構造出所有必要元素”,要么(當有任何一個copy constructor失 敗時)“不構造任何東西””

2.3.2 u n in itia liz e d _fill

  • ?&*i? ?其實就是元素的位置,*i代表元素的數值,加上&,就是取地址,后面接入x,construct就是在指定的位置上填寫x
  • 與 uninitialized_copy() 一樣,uninitialized_f ill ()必須具備 acommit or rollback語意,換句話說,它要么產生出所有必要元素,要么不產生任何元素。如果有任何一個 copy constructor 丟出異常(exception) ,uninitialized_f ill (),必須能夠將已產生的所有元素析構掉。

u n in itia liz e d _ fill_ n

  • ?uninitialized_fill_n ()能夠使我們將內存配置與對象構造行為分離開來。它會為指定范圍內的所有元素設定相同的初值。

  • fill_n
  • 如果是POD類型,使用_true_type,交由高階函數執行,使用fill_n對每個元素進行賦值
  • 如果不是POD類型,使用_flase_type,調用construct函數對每個元素進行賦值

?uninitialized_copy

  • ?這個函數的進行邏輯是,首先萃取出迭代器result的 value type (詳見第3 章 ) , 然后判斷該型別是否為PO D 型別:

  • ?如果是POD類型,采用最有效率的辦法就是復制的方式,內部調用fill_n對元素進行復制
  • 如果不是POD類型,只能使用最保險安全的construct的函數構造的方式

  • char是一個字節
  • wchar_t? 使用sizeof判斷不同環境下的占據的單個元素的空間大小
  • 使用memmove 直接對指定的內存區間進行數據的移動?

u n in itia liz e d _ fill

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

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

相關文章

中科大 計算機網絡7 分組延遲 分組丟失 吞吐量

分組丟失和延遲的原因 隊列太長沒有意義&#xff0c;用戶需求 排隊&#xff1a;輸出能力<到來的分組&#xff0c;需要等待 四種分組延遲 節點處理延遲&#xff1a;確定的 排隊延遲&#xff1a;隨機&#xff0c;取決于網絡情況 一個比特的傳輸時間&#xff1a; R1Mbps …

STL源碼剖析 迭代器iterator的概念 和 traits編程技法

iterator模式定義如下&#xff1a;提供一種方法&#xff0c;使之能夠依序巡訪某個 聚合物(容器)所含的各個元素&#xff0c;而又無需暴露該聚合物的內部表述方式.STL的中心思想在于&#xff1a;將數據容器(containers)和算法(algorithms)分開&#xff0c;彼此獨立設計&#xff…

中科大 計算機網絡11 應用層原理

應用層大綱 傳輸層向應用層提供的服務&#xff0c;形式是Socket API&#xff08;原語&#xff09; 一些網絡應用的例子 互聯網層次中&#xff0c;應用層協議最多 流媒體應用&#xff1a;直播 網絡核心最高的層次就是網絡層 應用進程通信方式 C/S&#xff1a; 客戶端&…

STL源碼剖析 序列式容器 vector 和 ilist

Vector list 單向鏈表 ilistlist的刪除操作&#xff0c;也只有指向被刪除元素的迭代器會失效&#xff0c;其他迭代器不會受到影響

中科大 計算機網絡5 接入網和物理媒體

接入網 接入網&#xff1a;把邊緣&#xff08;主機&#xff09;接入核心&#xff08;路由器&#xff0c;交換機&#xff09; 骨干網【連接主機和主機】和接入網中都有物理媒體 接入方式&#xff1a;有線和無線 帶寬共享/獨享 接入網&#xff1a;住宅接入modem modem調制解調…

STL源碼剖析 序列式容器 deque雙端隊列

相較于vector的內存拷貝&#xff0c;deque在內存不足時只需要進行內存的拼接操作即可&#xff0c;不需要重新配置、復制、釋放等操作&#xff0c;代價就是迭代器的架構不是一個普通的指針&#xff0c;比較復雜d e q u e 的迭代器 deque是分段連續空間。維持其“整體連續”假象…

中科大 計算機網絡6 Internet結構和ISP

互聯網的結構 端系統通過接入ISPs接入互聯網 n個ISP互相連接&#xff1a; IXP,Internet exchage point:互聯網接入點&#xff0c;互聯網交互點 ISP&#xff1a;互聯網服務提供商&#xff0c;提供接入&#xff0c;提供網絡【中國移動&#xff0c;中國電信】 ICP&#xff1a…

STL源碼剖析 Stack棧 queue隊列

隨機迭代器用于隨機數據訪問&#xff0c;所以棧stack不具備此功能

中科大 計算機網絡8 協議層次和服務模型

協議層次 協議層次&#xff1a;現實生活中的例子 分層 分層處理和實現復雜系統 圖中&#xff0c;左邊是模塊&#xff0c;右邊是分層 計算機的設計是分層&#xff0c;每一層實現一個或一組功能&#xff0c;下層向上層提供服務&#xff1b;但效率比較低 對等層實體通過協議來交換…

STL源碼剖析 heap堆結構

heap一般特指max-heap&#xff0c;即最大的元素位于heap和array的首部 heap不提供遍歷功能&#xff0c;也不提供迭代功能

中科大 計算機網絡9 互聯網歷史

總綱 計算機網絡 早期1960以前 1961-1972 NCP協議&#xff1a;相當于現在的TCP和IP協議 每個節點即是數據的源也是數據的目標

STL源碼剖析 序列式容器 slist

STL l i s t 是個雙向鏈表(double linked lis t) 。SGI STL提供了一個單向鏈 表 (single linked lis t) , 名 為 slist s l i s t 和 l i s t 的主要差別在于&#xff0c;前者的迭代器屬于單向的Forwardlterotor, 后者的迭代器屬于雙向的Bidirectional Iterator.為此&#xff0…

中科大 計算機網絡12 Web和HTTP

Web與HTTP 對象&#xff1a;web頁中其實是對象鏈接 URL&#xff1a;通用資源定位符【任何對象都可以使用URL來唯一標識】 用戶名&#xff1a;口令【支持匿名訪問&#xff0c;用戶名和口令不計】 端口&#xff1a;HTTP&#xff1a;80 FTP&#xff1a;21【使用默認端口號&#x…

STL源碼剖析 關聯式容器 樹 紅黑樹、二叉搜索樹、平衡二叉搜索樹

所謂關聯式容器&#xff0c;觀念上類似關聯式數據庫(實際上則簡單許多)&#xff1a;每筆數據(每個元素)都有一個鍵值(key)和一個實值(value) 2。當元素被插入到關聯式 容器中時&#xff0c;容器內部結構(可能是RB-tree,也可能是hash-table)便依照其鍵 值大小&#xff0c;以某種…

北京大學 軟件工程1 軟件 軟件工程 軟件開發 軟件工程框架

軟件的定義 重新定義軟件 新一代信息技術 區塊鏈 創造性思維 軟件的特點 軟件的種類 支撐軟件&#xff1a;VC&#xff0c;PyCharm等 應用軟件&#xff1a;QQ&#xff0c;微信 軟件工程的起源 軟件開發的三個階段 軟件工程概念的提出 軟件工程的定義 軟件工程將系統化&#…

java學習_Python基礎學習教程:從0學爬蟲?讓爬蟲滿足你的好奇心

Python基礎學習教程&#xff1a;從0學爬蟲&#xff1f;讓爬蟲滿足你的好奇心有必要學爬蟲嗎&#xff1f;我想&#xff0c;這已經是一個不需要討論的問題了。爬蟲&#xff0c;“有用”也“有趣”&#xff01;這個數據為王的時代&#xff0c;我們要從這個龐大的互聯網中來獲取到我…

安卓rom制作教程_安卓手機TWRP_Recovery卡刷圖文教程 適用于卡刷ROM,TWRP救磚

掃一掃二維碼&#xff0c;關注我&#xff0c;解決刷機各種疑難雜癥 ROM樂園獨家支持最近有很多小伙伴問怎么去卡刷&#xff0c;卡刷的操作是什么&#xff0c;什么是卡刷&#xff0c;小編就仔細來寫一下卡刷教程吧&#xff0c;記住&#xff0c;我們所說的卡刷&#xff0c;并不是…

東軟 軟件工程1 軟件危機 軟件工程 軟件生命周期

軟件危機 軟件危機產生的原因 消除軟件危機的途徑&#xff1a; 軟件工程歷史 軟件工程的概念 軟件工程項目的基本目標 軟件工程的基本原理 軟件生命周期 軟件工程的中的軟件生命周期