ptmalloc總結


內存管理的一般方法

  1. C 風格的內存管理程序主要實現 malloc()和 free()函數。
  2. 內存池是一種半內存管理方法。Apache 使用了池式內存(pooled memory),將其連接拆分為各個階段,每個階段都有自己 的內存池。在結束每個階段時,會一次釋放所有內存。
  3. 引用計數
  4. 垃圾收集。
    垃圾收集(Garbage collection)是全自動地檢測並移除不再使用的數據對象。垃圾收集 器通常會在當可用內存減少到少於一個具體的閾值時運行。通常,它們以程序所知的可用的 一組“基本”數據——棧數據、全局變量、寄存器——作為出發點。然后它們嘗試去追蹤通 過這些數據連接到每一塊數據。收集器找到的都是有用的數據;它沒有找到的就是垃圾,可 以被銷毀並重新使用這些無用的數據。

Glibc存在的某個問題

Glibc 在內存回收方面做得不太好,常見的一個問題,申請很多內存,然后又釋放,只是有一小塊 沒釋放,這時候 Glibc 就必須要等待這一小塊也釋放了,也把整個大塊釋放,極端情況下, 可能會造成幾個 G 的浪費。

內存管理數據結構概述

Main_arena 與 non_main_arena

每個進程只有一個主分配區,但可能存在多個非主分配區,ptmalloc 根據系統對分配區 的爭用情況動態增加非主分配區的數量,分配區的數量一旦增加,就不會再減少了。

主分配區可以訪問進程的 heap 區域和 mmap 映射區域,也就是說主分配區可以使用 sbrk 和 mmap 向操作系統申請虛擬內存。非主分配區只能使用mmap向操作系統申請虛擬內存。

而且程序線程很多的情況下,鎖等待的時間就會 延長,導致 malloc 性能下降。一次加鎖操作需要消耗 100ns 左右,正是鎖的緣故,導致 ptmalloc 在多線程競爭情況下性能遠遠落后於 tcmalloc。

chunk的組織

不管內存是在哪里被分配的,用什么方法分配,用戶請求分配的空間在 ptmalloc 中都使 用一個 chunk 來表示。用戶調用 free()函數釋放掉的內存也並不是立即就歸還給操作系統, 相反,它們也會被表示為一個 chunk,ptmalloc 使用特定的數據結構來管理這些空閑的 chunk。
ptmalloc 在給用戶分配的空間的前后加上了一些控制信息,用這樣的方法來記錄分配的
信息,以便完成分配和釋放工作。

空閑 chunk 容器

用戶 free 掉的內存並不是都會馬上歸還給系統,ptmalloc 會統一管理 heap 和 mmap 映
射區域中的空閑的 chunk,當用戶進行下一次分配請求時,ptmalloc 會首先試圖在空閑的 chunk 中挑選一塊給用戶,這樣就避免了頻繁的系統調用,降低了內存分配的開銷。ptmalloc 將相似大小的 chunk 用雙向鏈表鏈接起來,這樣的一個鏈表被稱為一個 bin。Ptmalloc 一共 維護了 128 個 bin,並使用一個數組來存儲這些bin

image

  1. 數組中的第一個為 unsorted bin
  2. 數組中從編號2到編號為64的bin 稱為 small bins,同 一個 small bin 中的 chunk 具有相同的大小。相鄰的small bins中的chunk大小相差為8B。
  3. Small bins 后面的 bin 被稱作 large bins。large bins 中的每一個 bin 分別包含了一個給定范圍 內的 chunk,其中的 chunk 按大小序排列。相同大小的 chunk 同樣按照最近使用順序排列。

Fast Bins

ptmalloc 中在分配過程中 引入了 fast bins,不大於 max_fast(默認值為 64B)的 chunk 被釋放后,首先會被放到 fast bins 中

Unsorted Bin

unsorted bin 的隊列使用 bins 數組的第一個,如果被用戶釋放的 chunk 大於 max_fast,或者 fast bins 中的空閑 chunk 合並后,這些 chunk 首先會被放到 unsorted bin 隊列中。在進 行 malloc 操作的時候,如果在 fast bins 中沒有找到合適的 chunk,則 ptmalloc 會先在 unsorted bin 中查找合適的空閑 chunk,然后才查找 bins。
如果 unsorted bin 不能滿足分配要求。malloc 便會將 unsorted bin 中的 chunk 加入 bins 中。然后再從 bins 中繼續進行查找和分配過程。

從這個過程可以看出來,unsorted bin可以看做是bins的一個緩沖區,增加它只是為了加快分配的速度。

Top chunk

top chunk對於主分配區和非主分配區是不一樣的。

  1. 非主分配區

對於非主分配區會預先從 mmap 區域分配一塊較大的空閑內存模擬 sub-heap, 通過管
理 sub-heap 來響應用戶的需求,因為內存是按地址從低向高進行分配的,在空閑內存的最高處, 必然存在着一塊空閑 chunk, 叫做 top chunk。當bins和fast bin都滿足不了用戶的需求,ptmalloc會從top chunk分出一塊內存給用戶,如果top chunk空間不足,會重新分配一個sub-heap,將top chunk遷移到行的sub-heap上。新的sub-heap和舊的sub。在分配過程中,top chunk的大小隨着切割動態變化。
2. 主分配區

主分配區是唯一能夠映射進程 heap 區域的分配區,它可以通過 sbrk()來增大或是
收縮進程 heap 的大小。top chunk在heap的最上面,如果申請內存時,top chunk空間不足,ptmalloc
會調用 sbrk()將的進程 heap 的邊界 brk 上移,然后修改 top chunk 的大小。

mmaped chunk

當分配的內存空間過大的時候,top chunk也不能滿足需求。ptmalloc會直接調用mmap給用戶分配內存。

mmap分配閾值( mmap threshold,默認值為 128KB,分配閾值可以動態調整。如果開啟了 mmap 分配
閾值的動態調整機制,並且當前回收的 chunk 大小大於 mmap 分配閾值,將 mmap
分配閾值設置為該 chunk 的大小,將 mmap 收縮閾值設定為 mmap 分配閾值的 2
倍。

last remainder

last remainder也不存在於bins中,當用戶在small bins中找不到合適的chunk,如果last remainder的大小大於small chunk的大小,last remainder會分裂為兩個chunk,一個返回給用戶,另一個變成新的remainder chunk。

內存分配概述

以32位系統為例

  • 小於等於 64 字節:用 pool 算法分配。
  • 64 到 512 字節之間:在最佳匹配算法分配和 pool 算法分配中取一種合適的。
  • 大於等於 512 字節:用最佳匹配算法分配。
  • 大於等於 mmap 分配閾值 (默認值 128KB): 根據設置的 mmap 的分配策略進行分配,
    如果沒有開啟 mmap 分配閾值的動態調整機制,大於等於 128KB 就直接調用 mmap20
    分配。 否則,大於等於 mmap 分配閾值時才直接調用 mmap()分配。

ptmalloc 的響應用戶內存分配要求的具體步驟

  1. 獲取一個未加鎖的分配區,如果所有分配區都加了鎖,ptmalloc會開辟一個新的分配區。開辟新分配區時,會調用mmap創建一個sub-heap,並設置好top chunk。
  2. 將用戶的請求大小轉換為實際需要分配的 chunk 空間大小
  3. 判斷所需分配 chunk的大小是否滿足 chunk_size <= max_fast (max_fast 默認為 64B),如果是的話, 則轉下一步, 否則跳到第 5 步。
  4. 首先嘗試在 fast bins 中取一個所需大小的 chunk 分配給用戶。 如果可以找到, 則分配結束。 否則轉到下一步。
  5. 判斷所需大小是否處在 small bins 中, 即判斷 chunk_size < 512B 是否成立。 如果chunk大小處在small bins中,則轉下一步,否則轉到第7步。
  6. 根據所需分配的 chunk 的大小, 找到具體所在的某個 small bin, 從該 bin 的尾部摘取一個恰好滿足大小的chunk。若成功,則分配結束,否則,轉到下一步。
  7. 首先盡可能將fast bins中的chunk合並,並且放入unsorted bin中。如果unsorted bin中只有一個chunk,而且在上次分配的時候使用過,並且說需要分配的屬於 small bins,並且 chunk 的大小大於等於需要分配的大小,這種情況下就直
    接將該 chunk 進行切割,分配結束。否則,將unsorted bin中的chunk放入small bins或者large bins。進入下一步。
  8. 從large bins分配一塊合適的chunk.
  9. 根據申請空間的大小和mmap分配閾值判斷,從top chunk中分配內存還是直接調用mmap分配內存。

總結:

小內存: [獲取分配區(arena)並加鎖] -> fast bins -> small bins -> 合並fast bins加入unsorted bins -> unsorted bins合並,加入small bins或者large bins -> small bins -> large bins -> top chunk(低於mmap閾值) -> mmap(高於mmap 閾值)

大內存: 直接mmap

內存回收概述

  1. 根據地址對齊找到sub-heap,從sub-heap頭部信息找到屬於哪個分配區,獲取分配區的鎖,保證線程安全。
  2. 判斷所需釋放的 chunk 是否為 mmaped chunk,如果是,則調用munmap()釋放mmaped chunk,解除內存空間映射,該該空間不再有效。
  3. 判斷 chunk 的大小和所處的位置,若 chunk_size <= max_fast,並且 chunk 並不位於heap 的頂部,也就是說並不與 top chunk 相鄰,則轉到下一步。否則跳到5。
  4. 將 chunk 放到 fast bins 中, chunk 放入到 fast bins 中時,並不會修改chunk的使用狀態位,也並不嘗試合並。然后free函數返回。
  5. 判斷前一個 chunk 是否處在使用中,如果前一個塊也是空閑塊,則合並。並轉下一步。
  6. 判斷當前釋放 chunk 的下一個塊是否為 top thunk,如果是,則轉第8步,否則轉下一步。
  7. 判斷下一個 chunk 是否處在使用中, 如果下一個 chunk 也是空閑的, 則合並, 並將合並后的 chunk 放到 unsorted bin 中。
  8. 將chunk與top chunk合並。
  9. 判斷合並后的chunk的大小是否大於FASTBIN_CONSOLIDATION_THRESHOLD(默認
    64KB), 如果是的話,則會觸發進行 fast bins 的合並操作, fast bins 中的 chunk 將被遍歷,並與相鄰的空閑 chunk 進行合並,合並后的 chunk 會被放到 unsorted bin 中。fast bins 將變為空, 操作完成之后轉下一步。
  10. 判斷top chunk是否大於mmap收縮值,如果大於就將一部分top chunk歸還給操作系統。

總結:

  1. 大內存:直接munmap
  2. 小內存(與top chunk相鄰):如果在top chunk上面,盡可能合並chunk,然后與top chunk合並。
  3. 小內存(chunk_size <= max_fast):直接放入fast_bin
  4. 小內存(chunk_size > max_fast):與周圍的chunk合並后放到unsorted bin中。
  5. (all)如果合並后的chunk觸發合並fast bin操作,合並fast bin放到unsorted中。

ptmalloc缺點

  1. 因為 ptmalloc 收縮內存是從top chunk開始,如果與top chunk相鄰的chunk不能釋放,top chunk以下都不能釋放。對於長期持有的內存,盡量直接越過mmap閾值調用mmap直接分配
  2. 多線程不友好,頻繁加鎖。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM