GLIBC記憶體分配機制引發的「記憶體洩露」

2021-09-23 21:39:45 字數 1907 閱讀 9818

我們正在開發的類資料庫系統有乙個記憶體模組,出現了乙個疑似」記憶體洩露」問題,現象如下:記憶體模組的記憶體釋放以後沒有歸還作業系統,比如記憶體模組占用的記憶體為10gb,釋放記憶體以後,通過top命令或者/proc/pid/status檢視占用的記憶體有時仍然為10g,有時為5g,有時為3g, etc,記憶體釋放的行為不確定。

首先說一下記憶體模組的記憶體管理機制。我們的記憶體管理很簡單,使用全域性的定長記憶體池,每乙個記憶體塊為64kb,如果申請的記憶體小於等於64kb時,直接從記憶體池的空閒鍊錶中獲取乙個記憶體塊,記憶體釋放時歸還空閒鍊錶;如果申請的記憶體大於64kb,直接通過作業系統的malloc和free獲取。某些資料結構涉及到很多小物件的管理,比如hash表,b-tree,這些資料結構從全域性記憶體池獲取記憶體後再根據資料結構的特點進行組織。為了提高記憶體申請/釋放的效率,減少鎖衝突,為每乙個執行緒單獨保留乙個8mb的記憶體塊,每個執行緒優先從執行緒專屬的8mb記憶體塊獲取記憶體,專屬記憶體不足時才從全域性的記憶體池獲取。

由於我們的所有記憶體申請/釋放操作都需要通過全域性的記憶體池進行,我們在全域性的記憶體池中加入對每個子模組的記憶體統計功能:每個子模組申請記憶體時都將子模組編號傳給全域性的記憶體池,全域性的記憶體池進行統計。復現問題後發現全域性的記憶體池的統計結果符合預期,因此懷疑是作業系統或者glibc的行為。

linux下glibc的記憶體管理機制大致如下:

我們的記憶體模組申請/釋放記憶體都是以2mb為單位的,按理說應該是使用mmap和munmap進行記憶體分配和釋放的,不會出現記憶體釋放以後仍然被程序占用的情況。在核心同學的協助下,經過長時間的分析定位,發現了glibc的新特性:m_mmap_threshold可以動態調整。m_mmap_threshold的值在128kb到32mb(32位機)或者64mb(64位機)之間動態調整,每次申請並釋放乙個大小為2mb的記憶體後,m_mmap_threshold的值被調整為2m到2m + 4k之間的乙個值(具體可以參考glibc的patch說明)。例如:

char* no_used = new char[2 * 1024 * 1024];

memset(no_used, 0xfe, 2 * 1024 * 1024);

delete no_used;

// m_mmap_threshold的值調整為2m到2m + 4k之間的乙個值,後續申請 <= 2 * 1024 * 1024的記憶體塊都會走sbrk而不是mmap

了解到這種現象後,我們找到了」記憶體洩露」的原因:m_mmap_threshold的值動態調整,後續的2mb的記憶體申請通過sbrk實現,而sbrk需要等到高位址記憶體釋放以後低位址記憶體才能釋放。可以通過顯式設定m_mmap_threshold或者m_mmap_max來關閉m_mmap_threshold動態調整的特性,從而避免上述問題。

當然,mmap呼叫是會導致程序產生缺頁中斷的,為了提高效能,常見的做法如下:

1, 將動態記憶體改為靜態,比如採用記憶體池技術或者啟動的時候給每個執行緒分配一定大小,比如8mb的記憶體,以後直接使用;

2, 禁止mmap記憶體呼叫,禁止glibc記憶體縮緊將記憶體歸還系統,glibc相當於實現了乙個記憶體池功能。只需要在程序啟動的時候加入兩行**:

mallopt(m_mmap_max, 0); // 禁止malloc呼叫mmap分配記憶體

mallopt(m_trim_threshold, 0); // 禁止記憶體縮排,sbrk申請的記憶體釋放後不會歸還給作業系統

花絮:

追查」記憶體洩露」問題的過程中,嘗試使用glibc的鉤子函式(malloc hook) 統計malloc和free的記憶體量:具體做法為malloc的時候多申請8個位元組,其中4個位元組記錄長度,4個位元組記錄magic_num,malloc和free的時候統計程序申請和釋放的記憶體量。實踐表明無論自定義鉤子函式是否加鎖,malloc和free鉤子函式在多執行緒的情況下執行都不正常,其它同學也發現了相同的問題(malloc hook多執行緒問題)。

C C 記憶體分配機制

1.c語言中的記憶體機制 在c語言中,記憶體主要分為如下5個儲存區 1 棧 stack 位於函式內的區域性變數 包括函式實參 由編譯器負責分配釋放,函式結束,棧變數失效。2 堆 heap 由程式設計師用malloc calloc realloc分配,free釋放。如果程式設計師忘記free了,則會造...

memcached記憶體分配機制

memcached slab allocator分配機制 slab allocator的基本原理是按照預先規定的大小,將分配的記憶體分割成特定長度的塊,以完全解決記憶體碎片問題。slab allocation的原理相當簡單,就是將分配的記憶體分割成各種尺寸的塊 chunk 並把尺寸相同的塊分成組 c...

Memcache記憶體分配機制

1.page 頁 為記憶體分配的最小單位 memcached 的記憶體分配以page為單位,預設情況下乙個page是1m,可以通過 i引數在啟動時指定。如果需要申請記憶體時,memcached會劃分出乙個新的page並分配給需要的slab區域。page一旦被分配在重啟前不會被 或者重新分配 2.sl...