淺析malloc的底層實現原理(ptmalloc)


內存佈局

介紹ptmalloc之前,我們首先來了解一下X86 平臺 Linux 進程內存佈局,以32位系統爲例:

從上圖可以看到,棧至頂向下擴展,堆至底向上擴展mmap 映射區域至頂向下擴展。 mmap 映射區域和堆相對擴展,直至耗盡虛擬地址空間中的剩餘區域,這種結構便於 C 運行時庫使用 mmap 映射區域和堆進行內存分配。


brk(sbrk)和mmap函數

首先,linux系統向用戶提供申請的內存有 brk(sbrk) 和 mmap 函數。下面我們先來了解一下這幾個函數。


brk() 和 sbrk()

#include <unistd.h>
int brk( const void *addr )
void* sbrk ( intptr_t incr );

兩者的作用是擴展heap的上界brk

brk()的參數設置爲新的brk上界地址,成功返回1,失敗返回0;

sbrk()的參數爲申請內存的大小,返回heap新的上界brk的地址


mmap()

#include <sys/mman.h>
void *mmap(void *addr, size\_t length,
int prot, int flags, int fd, off\_t offset);

int munmap(void *addr, size_t length);

mmap的第一種用法是映射磁盤文件到內存中;第二種用法是匿名映射不映射磁盤文件,而向映射區申請一塊內存

malloc使用的是mmap的第二種用法(匿名映射)。

munmap函數用於釋放內存。


內存管理的一般方法

1、C風格的內存管理

  • malloc:該函數分配給定的字節數,並返回一個指向它們的指針。 如果沒有足夠的可用內存,那麼它返回一個空指針。
  • free:該函數獲得指向由 malloc 分配的內存片段的指針,並將其釋放,以便以後的程序或操作系統使用(實際上,一些 malloc 實現只能將內存歸還給程序,而無法將內存歸還給操作系統)。

C 風格的內存管理程序主要實現 malloc()和 free()函數內存管理程序主要通過調用 brk() 或者 mmap()進程添加額外的虛擬內存。Doug Lea Malloc,ptmalloc,BSD malloc,Hoard,TCMalloc 都屬於這一類內存管理程序。

基於 malloc() 的內存管理器仍然有很多缺點,不管使用的是哪個分配程序。對於那些需要保持長期存儲的程序並不適合使用 malloc()來管理內存。如果有大量的不固定的內存引用,經常難以知道它們何時被釋放。生存期侷限於當前函數的內存非常容易管理,但是對於生存期超出該範圍的內存來說,管理內存則困難得多。因爲管理內存的問題,很多程序傾向於使用它們自己的內存管理規則。


2、池式內存管理

內存池是一種半內存管理方法。內存池幫助某些程序進行自動內存管理,這些程序會經歷一些特定的階段,而且每個階段中都有分配給進程的特定階段的內存。例如,很多網絡服務器進程都會分配很多針對每個連接的內存——內存的最大生存期限爲當前連接的存在期。例如:Apache 使用了池式內存(pooled memory),將其連接拆分爲各個階段,每個階段都有自己的內存池。在結束每個階段時,會一次釋放所有內存。


3、引用計數

在引用計數中,所有共享的數據結構都有一個域來包含當前活動“引用”結構的次數。當向一個程序傳遞一個指向某個數據結構指針時,該程序會將引用計數增加 1。實質上,是在告訴數據結構,它正在被存儲在多少個位置上。然後,當進程完成對它的使用後,該程序就會將引用計數減少 1。結束這個動作之後,它還會檢查計數是否已經減到零。如果引用計數爲零,那麼它將釋放內存


4、垃圾收集(Garbage collection)

垃圾收集是全自動地檢測並移除不再使用的數據對象。垃圾收集器通常會在當可用內存減少到少於一個具體的閾值時運行。通常,它們以程序所知的可用的一組“基本”數據:棧數據、全局變量、寄存器等作爲出發點。然後它們嘗試去追蹤通過這些數據連接到每一塊數據。收集器找到的都是有用的數據;它沒有找到的就是垃圾,可以被銷燬並重新使用這些無用的數據。爲了有效地管理內存,很多類型的垃圾收集器都需要知道數據結構內部指針的規劃,所以,爲了正確運行垃圾收集器,它們必須是語言本身的一部分。


malloc 實現原理

因爲brk、sbrk、mmap都屬於系統調用,若每次申請內存,都調用這三個,那麼每次都會產生系統調用,影響性能;其次,這樣申請的內存容易產生碎片,因爲堆是從低地址到高地址,如果高地址的內存沒有被釋放,低地址的內存就不能被回收。
  
因此,malloc採用的是內存池的管理方式(ptmalloc),ptmalloc 採用邊界標記法將內存劃分成很多塊,從而對內存的分配與回收進行管理。爲了內存分配函數malloc的高效性,ptmalloc會預先向操作系統申請一塊內存供用戶使用當我們申請和釋放內存的時候,ptmalloc會將這些內存管理起來,並通過一些策略來判斷是否將其回收給操作系統。這樣做的最大好處就是,使用戶申請和釋放內存的時候更加高效,避免產生過多的內存碎片。


ptmalloc簡介

ptmalloc 實現了 malloc(),free()以及一組其它的函數。以提供動態內存管理的支持。分配器處在用戶程序和內核之間,它響應用戶的分配請求,向操作系統申請內存,然後將其返回給用戶程序,爲了保持高效的分配,分配器一般都會預先分配一塊大於用戶請求的內存,並通過某種算法管理這塊內存,來滿足用戶的內存分配要求,用戶釋放掉的內存也並不是立即就返回給操作系統,相反,分配器會管理這些被釋放掉的空閒空間,以應對用戶以後的內存分配要求。

也就是說,分配器不但要管理已分配的內存塊,還需要管理空閒的內存塊,當響應用戶分配要求時,分配器會首先在空閒空間中尋找一塊合適的內存給用戶,在空閒空間中找不到的情況下才分配一塊新的內存。爲實現一個高效的分配器,需要考慮很多的因素。比如,分配器本身管理內存塊所佔用的內存空間必須很小,分配算法必須要足夠的快。


內存管理數據結構概述

主分配區與非主分配區

在 Doug Lea 實現的內存分配器中只有一個主分配區(main arena),每次分配內存都必須對主分配區加鎖,分配完成後釋放鎖,在 SMP 多線程環境下,對主分配區的鎖的爭用很激烈,嚴重影響了 malloc 的分配效率。主分配區與非主分配區用環形鏈表進行管理。每一個分配區利用互斥鎖(mutex)使線程對於該分配區的訪問互斥。

每個進程只有一個主分配區,但可能存在多個非主分配區,ptmalloc 根據系統對分配的爭用情況動態增加非主分配區的數量,分配區的數量一旦增加,就不會再減少了。主分配區可以訪問進程的 heap 區域和 mmap 映射區域,也就是說主分配區可以使用 sbrk 和 mmap向操作系統申請虛擬內存。而非主分配區只能訪問進程的 mmap 映射區域

主分配區可以訪問 heap 區域,如果用戶不調用 brk()或是 sbrk()函數,分配程序就可以保證分配到連續的虛擬地址空間,因爲每個進程只有一個主分配區使用 sbrk()分配 heap 區域的虛擬內存。內核對 brk 的實現可以看着是 mmap 的一個精簡版,相對高效一些。如果主分配區的內存是通過 mmap()向系統分配的,當 free 該內存時,主分配區會直接調用 munmap()將該內存歸還給系統

當某一線程需要調用 malloc()分配內存空間時,該線程先查看線程私有變量中是否已經存在一個分配區,如果存在,嘗試對該分配區加鎖,如果加鎖成功,使用該分配區分配內存,如果失敗,該線程搜索循環鏈表試圖獲得一個沒有加鎖的分配區如果所有的分配區都已經加鎖,那麼 malloc()會開闢一個新的分配區,把該分配區加入到全局分配區循環鏈表並加鎖,然後使用該分配區進行分配內存操作。在釋放操作中,線程同樣試圖獲得待釋放內存塊所在分配區的鎖,如果該分配區正在被別的線程使用,則需要等待直到其他線程釋放該分配區的互斥鎖之後纔可以進行釋放操作。

申請小塊內存時會產生很多內存碎片,ptmalloc 在整理時也需要對分配區做加鎖操作。每個加鎖操作大概需要 5~10 個 cpu 指令,而且程序線程很多的情況下,鎖等待的時間就會延長,導致 malloc 性能下降。一次加鎖操作需要消耗 100ns 左右,正是鎖的緣故,導致 ptmalloc在多線程競爭情況下性能遠遠落後於 tcmalloc。


chunk組織

用戶請求分配的空間在 ptmalloc 中都使用一個 chunk 來表示,用戶調用 free()函數釋放掉的內存也並不是立即就歸還給操作系統,相反,它們也會被表示爲一個 chunk,ptmalloc 使用特定的數據結構來管理這些空閒的 chunk。

ptmalloc 在給用戶分配的空間的前後加上了一些控制信息,用這樣的方法來記錄分配的信息,以便完成分配和釋放工作。一個使用中的 chunk(使用中,就是指還沒有被 free 掉)在內存中如下圖所示:

說明:

  • chunk指針指向chunk開始的地址mem指針指向用戶內存塊開始的地址
  • p=0時,表示前一個chunk爲空閒,prev_size纔有效
  • p=1時,表示前一個chunk正在使用,prev_size無效 p主要用於內存塊的合併操作;ptmalloc 分配的第一個塊總是將p設爲1, 以防止程序引用到不存在的區域
  • M=1 爲mmap映射區域分配;M=0爲heap區域分配
  • A=0 爲主分配區分配;A=1 爲非主分配區分配。

空閒 chunk 在內存中的結構如下圖所示:

說明:

  • 當chunk空閒時,其M狀態是不存在的,只有AP狀態
  • 原本是用戶數據區的地方存儲了四個指針
  • 指針fd指向後一個空閒的chunk,而bk指向前一個空閒的chunk,malloc通過這兩個指針將大小相近的chunk連成一個雙向鏈表
  • 在large bin中的空閒chunk,還有兩個指針,fd_nextsize和bk_nextsize,用於加快在large bin中查找最近匹配的空閒chunk。不同的chunk鏈表又是通過bins或者fastbins來組織的。

空閒鏈表 bins

用戶 free 掉的內存並不是都會馬上歸還給系統,ptmalloc 會統一管理 heap 和 mmap 映射區域中的空閒的 chunk,當用戶進行下一次分配請求時,ptmalloc 會首先試圖在空閒的chunk 中挑選一塊給用戶,這樣就避免了頻繁的系統調用,降低了內存分配的開銷

ptmalloc 將相似大小的 chunk 用雙向鏈表鏈接起來,這樣的一個鏈表被稱爲一個 bin。ptmalloc 一共維護了 128 個 bin,每個bins都維護了大小相近的雙向鏈表的chunk。基於chunk的大小,有下列幾種可用bins:

  • Fast bin
  • Unsorted bin
  • Small bin
  • Large bin

ptmalloc 使用一個數組來存儲這些 bin,如下圖所示:

數組中的第一個爲 unsorted bin,數組中從 2 開始編號的前 64 個 bin 稱爲 small bins同一個small bin中的chunk具有相同的大小。兩個相鄰的small bin中的chunk大小相差8bytes。small bins 中的 chunk 按照最近使用順序進行排列(FIFO),最後釋放的 chunk 被鏈接到鏈表的頭部,而申請 chunk 是從鏈表尾部開始,這樣,每一個 chunk 都有相同的機會被 ptmalloc 選中。

Small bins 後面的 bin 被稱作 large bins。large bins 中的每一個 bin 分別包含了一個給定範圍內的 chunk,其中的 chunk 按大小序排列。相同大小的 chunk 同樣按照最近使用順序排列。

ptmalloc 使用smallest-first,best-fit(以最小塊優先,最佳適合)原則在空閒 large bins 中查找合適的 chunk。

當空閒的 chunk 被鏈接到 bin 中的時候,ptmalloc 會把表示該 chunk 是否處於使用中的標誌 P 設爲 0(注意,這個標誌實際上處在下一個 chunk 中),同時 ptmalloc 還會檢查它前後的 chunk 是否也是空閒的,如果是的話,ptmalloc 會首先把它們合併爲一個大的 chunk,然後將合併後的 chunk 放到 unstored bin 中

要注意的是,並不是所有的 chunk 被釋放後就立即被放到 bin 中。ptmalloc 爲了提高分配的速度,會把一些小的的 chunk 先放到一個叫做fast bins 的容器內。


Fast Bins

一般的情況是,程序在運行時會經常需要申請和釋放一些較小的內存空間。當分配器合併了相鄰的幾個小的 chunk 之後,也許馬上就會有另一個小塊內存的請求,這樣分配器又需要從大的空閒內存中切分出一塊,這樣無疑是比較低效的,故而,ptmalloc 中在分配過程中引入了 fast bins,不大於 max_fast (默認值爲 64B)的 chunk 被釋放後,首先會被放到 fast bins 中,fast bins 中的 chunk 並不改變它的使用標誌 P。這樣也就無法將它們合併。

當需要給用戶分配的 chunk 小於或等於 max_fast 時,ptmalloc 首先會在 fast bins 中查找相應的空閒塊,然後纔會去查找bins中的空閒chunk。在某個特定的時候,ptmalloc會遍歷fast bins中的chunk,將相鄰的空閒 chunk 進行合併,並將合併後的 chunk 加入 unsorted bin 中,然後再將 usorted bin 裏的 chunk 加入 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 的一個緩衝區,增加它只是爲了加快分配的速度。


small bins

大小 &lt;512&lt;512 字節的chunk被稱爲small chunk,而保存small chunks的bin被稱爲small bin。數組從2開始編號,前64個bin爲small bins,small bin每個bin之間相差8個字節,同一個small bin中的chunk具有相同大小。

每個small bin都包括一個空閒區塊的雙向循環鏈表(也稱binlist)。free掉的chunk添加在鏈表的前端,而所需chunk則從鏈表後端摘除

兩個毗連的空閒chunk會被合併成一個空閒chunk。合併消除了碎片化的影響但是減慢了free的速度。

分配時,當samll bin非空後,相應的bin會摘除binlist中最後一個chunk並返回給用戶。在free一個chunk的時候,檢查其前或其後的chunk是否空閒,若是則合併,也即把它們從所屬的鏈表中摘除並合併成一個新的chunk,新chunk會添加在unsorted bin鏈表的前端。


large bins

大小大 512≥512 字節的chunk被稱爲large chunk,而保存large chunks的bin被稱爲large bin,位於small bins後面。large bins中的每一個bin分別包含了一個給定範圍內的chunk,其中的chunk按大小遞減排序,大小相同則按照最近使用時間排列。

兩個毗連的空閒chunk會被合併成一個空閒chunk。

ptmalloc 使用smallest-first,best-fit(以最小塊優先,最佳適合)原則從頂部遍歷到底部以找到一個大小最接近用戶需求的chunk。一旦找到,相應chunk就會分成兩塊User chunk(用戶請求大小)返回給用戶


並不是所有chunk都按照上面的方式來組織,有三種例外情況。

  • top chunk
  • mmaped chunk
  • last remainder chunk

Top chunk

top chunk相當於分配區的頂部空閒內存,當bins上都不能滿足內存分配要求的時候,就會來top chunk上分配。

當top chunk大小比用戶所請求大小還大的時候,top chunk會分爲兩個部分:User chunk(用戶請求大小)和Remainder chunk(剩餘大小)。其中Remainder chunk成爲新的top chunk。

當top chunk大小小於用戶所請求的大小時,top chunk就通過sbrk(main arena)或mmap(thread arena)系統調用來擴容


mmaped chunk

當需要分配的 chunk 足夠大(大於分配閥值,默認128K),而且 fast bins 和 bins 都不能滿足要求,甚至 top chunk 本身也不能滿足分配需求時,ptmalloc 會使用 mmap 來直接使用內存映射來將頁映射到進程空間,則會放到mmaped chunk上

當釋放mmaped chunk上的內存的時候會直接交還給操作系統。


Last remainder chunk

Last remainder chunk是另外一種特殊的chunk,就像top chunk和mmaped chunk一樣,不會在任何bins中找到這種chunk。當需要分配一個small chunk,但在small bins中找不到合適的chunk,如果last remainder chunk的大小大於所需要的small chunk大小,last remainder chunk被分裂成兩個chunk,其中一個chunk返回給用戶,另一個chunk變成新的last remainder chunk


sbrk 與 mmap

在堆區中,start_brk 指向 heap 的開始,而 brk 指向 heap 的頂部。可以使用系統調用 brk() 和 sbrk() 來增加標識 heap 頂部的 brk 值,從而線性的增加分配給用戶的 heap 空間。在使 malloc 之前,brk 的值等於 start_brk,也就是說 heap 大小爲 0。

ptmalloc 在開始時,若請求的空間小於 mmap 分配閾值(mmap threshold,默認值爲 128KB)時,主分配區會調用 sbrk()增加一塊大小爲 (128 KB + chunk_size) align 4KB 的空間作爲 heap。非主分配區會調用 mmap 映射一塊大小爲 HEAP_MAX_SIZE(32 位系統上默認爲 1MB,64 位系統上默認爲 64MB)的空間作爲 sub-heap。這就是前面所說的 ptmalloc 所維護的分配空間。

當用戶請求內存分配時,首先會在這個區域內找一塊合適的 chunk 給用戶。當用戶釋放了 heap 中的 chunk 時,ptmalloc 又會使用 fastbins 和 bins 來組織空閒 chunk。以備用戶的下一次分配

若需要分配的 chunk 大小小於 mmap分配閾值,而 heap 空間又不夠,則此時主分配區會通過 sbrk()調用來增加 heap 大小非主分配區會調用 mmap 映射一塊新的 sub-heap,也就是增加 top chunk 的大小,每次 heap 增加的值都會對齊到 4KB

當用戶的請求超過 mmap 分配閾值,並且主分配區使用 sbrk()分配失敗的時候,或是非主分配區在 top chunk 中不能分配到需要的內存時,ptmalloc 會嘗試使用 mmap()直接映射一塊內存到進程內存空間使用 mmap()直接映射的 chunk 在釋放時直接解除映射,而不再屬於進程的內存空間。任何對該內存的訪問都會產生段錯誤。而在 heap 中或是 sub-heap 中分配的空間則可能會留在進程內存空間內,還可以再次引用(當然是很危險的)。


內存分配概述

分配算法概述,以 32 系統爲例,64 位系統類似。

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

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

  1. 獲取分配區的鎖,防止多線程衝突。
  2. 計算出實際需要分配的內存的chunk實際大小。
  3. 判斷chunk的大小,如果小於max_fast(64B),則嘗試去fast bins上取適合的chunk,如果有則分配結束。否則,下一步;
  4. 判斷chunk大小是否小於512B,如果是,則從small bins上去查找chunk,如果有合適的,則分配結束。否則下一步;
  5. ptmalloc首先會遍歷 fast bins 中的chunk,將相鄰的chunk進行合併,並鏈接到unsorted bin 中然後遍歷 unsorted bins。如果 unsorted bins 上只有一個chunk並且大於待分配的chunk,則進行切割,並且剩餘的chunk繼續扔回 unsorted bins;如果 unsorted bins 上有大小和待分配chunk相等的,則返回,並從 unsorted bins刪除;如果 unsorted bins 中的某一chunk大小屬於 small bins 的範圍,則放入small bins的頭部;如果 unsorted bins 中的某一chunk大小屬於 large bins 的範圍,則找到合適的位置放入。若未分配成功,轉入下一步;
  6. 從 large bins 中查找找到合適的chunk之後,然後進行切割,一部分分配給用戶,剩下的放入 unsorted bin 中。
  7. 如果搜索 fast bins 和 bins 都沒有找到合適的chunk,那麼就需要操作top chunk來進行分配了。當 top chunk 大小比用戶所請求大小還大的時候,top chunk會分爲兩個部分:User chunk(用戶請求大小)和 Remainder chunk(剩餘大小)。其中 Remainder chunk 成爲新的 top chunk。 當 top chunk 大小小於用戶所請求的大小時,top chunk就通過sbrk(main arena)或mmap(thread arena)系統調用來擴容。
  8. 到了這一步,說明 top chunk 也不能滿足分配要求,所以,於是就有了兩個選擇: 如果是主分配區,調用 sbrk(),增加 top chunk 大小;如果是非主分配區,調用 mmap 來分配一個新的 sub-heap,增加 top chunk 大小;或者使用 mmap()來直接分配。在這裏,需要依靠 chunk 的大小來決定到底使用哪種方法。判斷所需分配的 chunk 大小是否大於等於 mmap 分配閾值,如果是的話,則轉下一步,調用 mmap 分配, 否則跳到第 10 步,增加 top chunk 的大小。
  9. 使用 mmap 系統調用爲程序的內存空間映射一塊 chunk_size align 4kB 大小的空間。 然後將內存指針返回給用戶。
    10.判斷是否爲第一次調用 malloc,若是主分配區,則需要進行一次初始化工作,分配一塊大小爲(chunk_size + 128KB) align 4KB 大小的空間作爲初始的 heap。若已經初始化過了,主分配區則調用 sbrk()增加 heap 空間,分主分配區則在 top chunk 中切 割出一個 chunk,使之滿足分配需求,並將內存指針返回給用戶。

簡而言之: 獲取分配區(arena)並加鎖 fast binunsorted binsmall binlarge bintop chunk擴展堆


內存回收概述

free() 函數接受一個指向分配區域的指針作爲參數,釋放該指針所指向的 chunk

而具體的釋放方法則根據該 chunk 所處的位置和該 chunk 的大小而定。free()函數的工作步驟如下:

  1. 獲取分配區的鎖,保證線程安全。
  2. 如果free的是空指針,則返回,什麼都不做。
  3. 判斷當前 chunk 是否是 mmap 映射區域映射的內存,如果是,則直接munmap()釋放這塊內存。前面的已使用chunk的數據結構中,我們可以看到有M來標識是否是mmap映射的內存
  4. 判斷 chunk 是否與 top chunk 相鄰,如果相鄰,則直接和top chunk合併(和top chunk相鄰相當於和分配區中的空閒內存塊相鄰)。轉到步驟8
  5. 如果chunk的大小大於max_fast(64b),則放入unsorted bin,並且檢查是否有合併,有合併情況並且和 top chunk 相鄰,則轉到步驟8;沒有合併情況則free。
  6. 如果chunk的大小小於 max_fast(64b),則直接放入fast bin,fast bin並沒有改變chunk的狀態。沒有合併情況,則free;有合併情況,轉到步驟7
  7. 在fast bin,如果當前chunk的下一個chunk也是空閒的,則將這兩個chunk合併,放入unsorted bin上面。合併後的大小如果大於64B,會觸發進行fast bins的合併操作,fast bins中的chunk將被遍歷,並與相鄰的空閒chunk進行合併,合併後的chunk會被放到unsorted bin中,fast bin會變爲空。合併後的chunk和topchunk相鄰,則會合併到topchunk中。轉到步驟8
  8. 判斷 top chunk 的大小是否大於 mmap 收縮閾值(默認爲128KB),如果是的話,對於主分配區,則會試圖歸還 top chunk 中的一部分給操作系統。free結束。

使用注意事項

爲了避免Glibc內存暴增,需要注意:

  1. 後分配的內存先釋放,因爲ptmalloc收縮內存是從top chunk開始,如果與top chunk相鄰的chunk不能釋放,top chunk以下的chunk都無法釋放。
  2. ptmalloc不適合用於管理長生命週期的內存,特別是持續不定期分配和釋放長生命週期的內存,這將導致ptmalloc內存暴增。
  3. 不要關閉 ptmalloc 的 mmap 分配閾值動態調整機制,因爲這種機制保證了短生命週期的 內存分配儘量從 ptmalloc 緩存的內存 chunk 中分配,更高效,浪費更少的內存。
  4. 多線程分階段執行的程序不適合用ptmalloc,這種程序的內存更適合用內存池管理 。
  5. 儘量減少程序的線程數量和避免頻繁分配/釋放內存。頻繁分配,會導致鎖的競爭,最終導致非主分配區增加,內存碎片增高,並且性能降低。
  6. 防止內存泄露,ptmalloc對內存泄露是相當敏感的,根據它的內存收縮機制,如果與top chunk相鄰的那個chunk沒有回收,將導致top chunk一下很多的空閒內存都無法返回給操作系統。
  7. 防止程序分配過多的內存,或是由於glibc內存暴增,導致系統內存耗盡,程序因爲OOM被系統殺掉。預估程序可以使用的最大物理內存的大小,配置系統的/proc/sys/vm/overcommit_memory ,/proc/sys/vm/overcommit_ratio,以及使用ulimit -v限制程序能使用的虛擬內存的大小,防止程序因OOM被殺死掉。

參考:
華庭 《Glibc 內存管理——Ptmalloc2 源代碼分析》
《ptmalloc代碼淺析2(small bin/large bin結構圖)》
《malloc的底層實現(ptmalloc)》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章