【C++內存管理】G2.9 std::alloc 運行模式

上一篇裏面說到,vs 的標準分配器 std::allocator 並沒有做內存管理。gnu的歷史版本 2.9 中的標準分配器 std::allocator 也沒有做內存管理,但是它提供的 extended allocator 中,std::alloc 就是一個做了內存管理的版本。在 gnu 新版本中,__gnu_cxx::__pool_alloc<_Ty> 就是 G2.9 的 std::alloc 的化身。


下面先分析以下 std::alloc 的運行模式,再下一篇分析具體代碼。
不過先需要前三篇的基礎:

內存管理實例一
內存管理實例二
內存管理實例三


數據結構

G2.9 std::alloc

  • 一個指針數組 free_list[16],分別負責16條不同大小的 block,以 8 bytes 爲間隔。比如 #0 負責 8 bytes 的 block,#3 負責 32 bytes 的 block。當客戶端需要 size 大小的內存時,首先被調整到 8 的倍數,然後到 free_list 相應的位置索取。
  • 當用戶所索取的 block 大小超過了 free_list 所能提供的最大的大小也就是 128 字節時,會轉而調用 malloc 函數。
  • 以用戶索取 size = 32 爲例。如果 #3 位置上沒有分配內存塊,那麼會一次性分配 20 * size * 2 + ROUNDUP() 這麼大的內存塊 (以下稱爲 chunk)。其中前面的 20 * size 大小的內存塊會被分割成 20 個 blocks,被串成鏈表,並且把第一塊 block 返回給客戶。剩下的 20 * size + ROUND() 大小的 chunk 會被用來當作 memory pool,其中有一個 start_freeend_free 指針分別指向這一塊內存的首尾位置。這一塊 memory pool 爲下一次用戶索取不同大小 size 的 block 做準備。ROUND() 是一個與當前分配內存總量相關的函數,下面再具體說明。
  • 每一次對內存塊進行切割成 block 串成鏈表,鏈表的長度不會超過 20。
  • 除了所分配的一整塊 chunk 的首尾 block 帶有 cookie 之外,其餘的都是 cookie free blocks。

實例分析


申請新的內存塊

在這裏插入圖片描述

  • 由於 此時 memory pool 爲空,所以利用 malloc 函數,向操作系統索取 32 * 20 * 2 + ROUNDUP (0 >> 4) = 1280 大小的 chunk。將前面的 640 bytes 大小切割成 20 個 block ,串成鏈表,並將第一個 block 返回給客戶。
  • start_freeend_free 指向剩餘 640 bytes 相對應的位置。
  • 這裏也可以看出,ROUNDUP 函數就是返回 當前已分配的內存總量 / 16 的結果。

在這裏插入圖片描述

  • 由於此時 memory pool 有640 bytes 的餘量,所以直接對這一塊 chunk 進行切割。剛好能夠切割 10 個 block (這也是爲什麼前面說,鏈表的長度最大爲 20)。返回第一個 block。
  • #3 和 # 7 所指向的內存塊,在物理上是連續的,但是在邏輯上,是不連續的。
  • 此時累計申請 1280 bytes,memory pool 餘量爲 0 byte

在這裏插入圖片描述

  • 由於此時 memory pool 爲空,又需要向操作系統索取 96 * 2 * 20 + ROUNDUP (1280 >> 4) = 3920 大小的 chunk。前 1920 串成鏈表,返回第一個,後 2000 作爲 memory pool。
  • 此時累計申請 5200 bytes,memory pool 餘量爲 2000 byte

客戶端連續申請

在這裏插入圖片描述
在這裏插入圖片描述


在這裏插入圖片描述

內存碎片的處理

在這裏插入圖片描述

  • 此時 memory pool 只有 80 bytes ,並不能夠分出一個 104 bytes 的 block,此時就產生了內存碎片對於內存碎片,會將它以一個單位大小的 block 撥給 free_list 相對應的位置。比如再這裏,會將這一塊 80 bytes 的內存塊撥給 #9 的位置。最後再來分配新的內存。

在這裏插入圖片描述


在這裏插入圖片描述

  • 此時 memory pool 只有 168 bytes,只能分出 3 個 48 bytes 的 block,剩下的 24 bytes 作爲 memory pool。
內存耗盡時

在這裏插入圖片描述

  • 此時 memory pool 只有 24 bytes ,並不能夠分出一個 72 bytes 的 block,這個時候又產生了內存碎片。相同的,首先將這 24 bytes 的內存塊撥到 #2 位置。然後再來索取新的內存塊。
  • 假設此時由於內存耗盡,操作系統無法一次性給予 72 * 20 * 2 + ROUNDUP(9688 >> 4) 這麼大的內存塊。這個時候,它就會向距離它最近的,比 block size 大的內存鏈表中索取一塊,從中切出 72 byte 的大小。這裏 #9 位置正好有一個 block ,它就會被轉接到 #8 位置上,原來 #9 的鏈表斷開。其中前 72 bytes 被返回給客戶,最後剩下的 8 bytes 作爲 memory pool。最後的結果就是 #8 #9 位置的鏈表都爲空了。

在這裏插入圖片描述

最終內存分配失敗

在這裏插入圖片描述

  • 此時操作系統的內存耗盡,無法獲取新的內存。而且也沒有可用的已經獲得的內存鏈表。最終導致客戶端內存分配失敗。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章