ptmalloc 是開源 GNU C Library (glibc) 默認(rèn)的內(nèi)存管理器,當(dāng)前大部分 Linux 服務(wù)端程序使用的是 ptmalloc 提供的 malloc/free 系列函數(shù),而它在性能上遠(yuǎn)差于 Meta 的 jemalloc 和 Google 的 tcmalloc。服務(wù)端程序調(diào)用 ptmalloc 提供的 malloc/free 函數(shù)申請和釋放內(nèi)存,ptmalloc 提供對內(nèi)存的集中管理,以盡可能達(dá)到:
十余年的平羅網(wǎng)站建設(shè)經(jīng)驗,針對設(shè)計、前端、開發(fā)、售后、文案、推廣等六對一服務(wù),響應(yīng)快,48小時及時工作處理。網(wǎng)絡(luò)營銷推廣的優(yōu)勢是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動調(diào)整平羅建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計,從而大程度地提升瀏覽體驗。創(chuàng)新互聯(lián)從事“平羅網(wǎng)站設(shè)計”,“平羅網(wǎng)站推廣”以來,每個客戶項目都認(rèn)真落實執(zhí)行。用戶申請和釋放內(nèi)存更加高效,避免多線程申請內(nèi)存并發(fā)和加鎖
尋求與操作系統(tǒng)交互過程中內(nèi)存占用和 malloc/free 性能消耗的平衡點,降低內(nèi)存碎片化,不頻繁調(diào)用系統(tǒng)調(diào)用函數(shù)
簡單概括 ptmalloc 的內(nèi)存管理策略:
預(yù)先向操作系統(tǒng)申請并持有一塊內(nèi)存供用戶 malloc,同時管理已使用和空閑的內(nèi)存
用戶執(zhí)行 free,會將回收的內(nèi)存管理起來,并執(zhí)行管理策略決定是否交還給操作系統(tǒng)
接下來,將從 ptmalloc 數(shù)據(jù)結(jié)構(gòu)、內(nèi)存分配及優(yōu)缺點介紹最經(jīng)典的 c++ 內(nèi)存管理器的實現(xiàn)和使用(以 32 位機(jī)為例)。
二、內(nèi)存管理 2.1 數(shù)據(jù)結(jié)構(gòu)為了解決多線程鎖爭奪問題,將內(nèi)存分配區(qū)分為主分配區(qū) (main_area) 和非主分配區(qū) (no_main_area)。同時,為了便于管理內(nèi)存,對預(yù)申請的內(nèi)存采用邊界標(biāo)記法劃分成很多塊 (chunk);ptmalloc 內(nèi)存分配器中,malloc_chunk 是基本組織單元,用于管理不同類型的 chunk,功能和大小相近的 chunk 串聯(lián)成鏈表,被稱為一個 bin。
main_arena 與 non_main_arena主分配區(qū)和非主分配區(qū)形成一個環(huán)形鏈表進(jìn)行管理, 每一個分配區(qū)利用互斥鎖實現(xiàn)線程對該分配區(qū)的訪問互斥。每個進(jìn)程只有一個主分配區(qū),但允許有多個非主分配區(qū),且非主分配區(qū)的數(shù)量只增加不減少。主分配區(qū)可以訪問進(jìn)程的 heap 區(qū)域和 mmap 映射區(qū)域,即主分配區(qū)可以使用 sbrk () 和 mmap () 分配內(nèi)存;非主分配區(qū)只能使用 mmap () 分配內(nèi)存。
對于不同 arena 的管理策略大致如下:
分配內(nèi)存
查看該線程的私有變量中是否已經(jīng)存在一個分配區(qū)并對其進(jìn)行加鎖操作,如果加鎖成功,則使用該分配區(qū)分配內(nèi)存;如果未找到該分區(qū)或加鎖失敗,遍歷環(huán)形鏈表中獲取一個未加鎖的分配區(qū)
如果整個環(huán)形鏈表中沒有未加鎖的分配區(qū),開辟一個新的分配區(qū),將其加入循環(huán)鏈表并加鎖,使用該分配區(qū)滿足當(dāng)前線程的內(nèi)存分配
釋放內(nèi)存
先獲取待釋放內(nèi)存塊所在的分配區(qū)的鎖,如果有其他線程正在使用該分配區(qū),等待其他線程釋放該分配區(qū)互斥鎖后,再釋放內(nèi)存
主分配區(qū)和非主分配區(qū)的結(jié)構(gòu)如下:
其中 fastbinsY 和 bins 是對實際內(nèi)存塊的管理和操作結(jié)構(gòu):
fastbinsY: 用以保存 fast bins
bins [NBINS * 2 - 2]: unsorted bin(1 個,bin [1])、small bins(62 個,bin [2]~bin [63])、large bins(63 個,bin [64]~bin [126])的集合,一共有 126 個表項 (NBINS = 128),bin [0] 和 bin [127] 沒有被使用
ptmalloc 統(tǒng)一管理 heap 和 mmap 映射區(qū)域中空閑的 chunk,當(dāng)用戶進(jìn)行分配請求時,會先試圖在空閑的 chunk 中查找和分割,從而避免頻繁的系統(tǒng)調(diào)用,降低內(nèi)存分配的開銷。為了更好的管理和查找空閑 chunk,在預(yù)分配的空間的前后添加了必要的控制信息,內(nèi)存管理結(jié)構(gòu) malloc_chunk 的成員及作用如下:
mchunk_prev_size: 前一個空閑 chunk 的大小
mchunk_size: 當(dāng)前 chunk 的大小
必要的屬性標(biāo)志位:
前一個 chunk 在使用中 (P = 1)
當(dāng)前 chunk 是 mmap 映射區(qū)域分配 (M = 1) 或是 heap 區(qū)域分配 (M = 0)
當(dāng)前 chunk 屬于非主分配區(qū) (A = 0) 或非主分配區(qū) (A = 1)
fd 和 bk: chunk 塊空閑時存在,用于將空閑 chunk 塊加入到空閑 chunk 塊鏈表中統(tǒng)一管理
基于 chunk 的大小和使用方法,劃分出以下幾種 bins:
fast bins
fast bins 僅保存很小的堆,采用單鏈表串聯(lián),增刪 chunk 都發(fā)生在鏈表的頭部,進(jìn)一步提高小內(nèi)存的分配效率。fast bins 記錄著大小以 8 字節(jié)遞增的 bin 鏈表,一般不會和其他堆塊合并。
unsorted bin
small bins 和 large bins 的緩沖區(qū),用于加快分配的速度,chunk 大小無尺寸限制,用戶釋放的堆塊,會先進(jìn)入 unsorted bin。分配堆塊時,會優(yōu)先檢查 unsorted bin 鏈表中是否存在合適的堆塊,并進(jìn)行切割并返回。
small bins
保存大小< 512B 的 chunk 的 bin 被稱為 small bins。small bins 每個 bin 之間相差 8 個字節(jié),同一個 small bin 中的 chunk 具有相同大小,采用雙向循環(huán)鏈表串聯(lián)。
large bins
保存大小 >= 512B 的 chunk 的 bin 被稱為 large bins。large bins 中的每一個 bin 分別包含了一個給定范圍內(nèi)的 chunk,其中的 chunk 按大小降序,相同大小按時間降序。
當(dāng)然,并不是所有 chunk 都按上述的方式來組織,其他常用的 chunk,如:
top chunk: 分配區(qū)的頂部空閑內(nèi)存,當(dāng) bins 不能滿足內(nèi)存分配要求的時候,會嘗試在 top chunk 分配。
當(dāng) top chunk >用戶請求大小,top chunk 會分為兩個部分:用戶請求大小 (user chunk) 和剩余 top chunk 大小 (remainder chunk)
當(dāng) top chunk< 用戶所請求大小,top chunk 就通過 sbrk(main_arena)或 mmap(non_main_arena)系統(tǒng)調(diào)用來擴(kuò)容
概括內(nèi)存 malloc 和 free 的流程大致如下:
內(nèi)存分配 malloc 流程1、獲取分配區(qū)的鎖
2、計算出需要分配的內(nèi)存的 chunk 實際大小
3、如果 chunk 的大小< max_fast,在 fast bins 上查找適合的 chunk;如果不存在,轉(zhuǎn)到 5
4、如果 chunk 大小< 512B,從 small bins 上去查找 chunk,如果存在,分配結(jié)束
5、需要分配的是一塊大的內(nèi)存,或者 small bins 中找不到 chunk:
a. 遍歷 fast bins,合并相鄰的 chunk,并鏈接到 unsorted bin 中
b. 遍歷 unsorted bin 中的 chunk:
①能夠切割 chunk 直接分配,分配結(jié)束
②根據(jù) chunk 的空間大小將其放入 small bins 或是 large bins 中,遍歷完成后,轉(zhuǎn)到 6
6、需要分配的是一塊大的內(nèi)存,或者 small bins 和 unsorted bin 中都找不到合適的 chunk,且 fast bins 和 unsorted bin 中所有的 chunk 已清除:
7、檢索 fast bins 和 bins 沒有找到合適的 chunk,判斷 top chunk 大小是否滿足所需 chunk 的大小,從 top chunk 中分配
8、top chunk 不能滿足需求,需要擴(kuò)大 top chunk:
a. 主分區(qū)上,如果分配的內(nèi)存<分配閾值(默認(rèn) 128KB),使用 brk () 分配;如果分配的內(nèi)存 >分配閾值,使用 mmap 分配
b. 非主分區(qū)上,使用 mmap 來分配一塊內(nèi)存
1、獲取分配區(qū)的鎖
2、如果 free 的是空指針,返回
3、如果當(dāng)前 chunk 是 mmap 映射區(qū)域映射的內(nèi)存,調(diào)用 munmap () 釋放內(nèi)存
4、如果 chunk 與 top chunk 相鄰,直接與 top chunk 合并,轉(zhuǎn)到 8
5、如果 chunk 的大小 >max_fast,放入 unsorted bin,并且檢查是否有合并:
a. 沒有合并情況則 free
b. 有合并情況并且和 top chunk 相鄰,轉(zhuǎn)到 8
6、如果 chunk 的大小< max_fast,放入 fast bin,并且檢查是否有合并:
a.fast bin 并沒有改變 chunk 的狀態(tài),沒有合并情況則 free
b. 有合并情況,轉(zhuǎn)到 7
7、在 fast bin,如果相鄰 chunk 空閑,則將這兩個 chunk 合并,放入 unsorted bin。如果合并后的大小 >64KB,會觸發(fā)進(jìn)行 fast bins 的合并操作,fast bins 中的 chunk 將被遍歷合并,合并后的 chunk 會被放到 unsorted bin 中。合并后的 chunk 和 top chunk 相鄰,則會合并到 top chunk 中,轉(zhuǎn)到 8
8、如果 top chunk 的大小 >mmap 收縮閾值(默認(rèn)為 128KB),對于主分配區(qū),會試圖歸還 top chunk 中的一部分給操作系統(tǒng)
三、優(yōu)缺點ptmalloc 作為 glibc 默認(rèn)的內(nèi)存管理器,已經(jīng)廣泛的滿足大多數(shù)大型項目的內(nèi)存管理,同時它的實現(xiàn)思路也對后來的內(nèi)存管理器提供了借鑒。
ptmalloc 的介紹暫告一段落,接下來的幾篇文章將繼續(xù)探討高性能內(nèi)存管理庫的集大成者 ——jemalloc、tcmalloc 內(nèi)存管理庫。
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧