glibc內存泄露以及TCmalloc 簡單分析

 

最近開發一個私人程序時碰到了嚴重的內存問題,具體表現爲:進程佔用的內存會隨着訪問高峯不斷上升,直到發生OOM被kill爲止。我們使用valgrind等工具進行檢查發現程序並無內存泄露,經過仔細調查我們發現時glibc的內存管理機制導致的,下次將發文對此深入解釋,本文只列出核心的幾個要素:

1. glibc在多線程內存分配的場景下爲了減少lock contention,會new出很多arena出來,每個線程都有自己默認的arena,但是內存申請時如果默認arena被佔用,則round-robin到下一個arena。

2. 每個arena的空間不可直接共享和互相借用,除非通過主arena釋放給操作系統然後被各個輔助arena重新申請。

3. glibc歸還內存給OS有一個很苛刻的條件就是top chunk必須是free的,否則,即使應用程序已經釋放了大片內存,glibc也不會將這些內存歸還給OS。

在我們的場景中常常是thread A alloc一片空間,最後由thread B free,所以這就造成各個arena之間及其不平衡,加上苛刻的內存歸還條件,在整個程序運行過程中,佔用內存幾乎從未下降過,區別僅僅是緩慢上漲和快速上漲。

由此我們實驗了tcmalloc,具體介紹見:http://google-perftools.googlecode.com/svn/trunk/doc/tcmalloc.html

安裝過程見其中的INSTALL文件,下面簡略說一下:

1. install libunwind : git clone git://git.sv.gnu.org/libunwind.git

2. download : http://google-perftools.googlecode.com/files/google-perftools-1.7.tar.gz

3. ./configure --enable-frame_pointers && make && sudo make install

4. sudo ldconfig

5. g++ .... -ltcmalloc (link static lib)

tcmalloc每個線程默認最大緩存16M空間,所以當線程多的時候其佔用的空間還是非常可觀的,在common.h中有幾個參數是控制緩存空間的,可以做合理的修改(只可個人做實驗,注意法律問題):

1. 降低每個線程的緩存空間,可以修改common.h中的kMaxThreadCacheSize,比如2M

2. 降低所有線程的緩存空間的總大小,可以修改common.h中的kDefaultOverallThreadCacheSize,比如20M

3. 儘快將free的空間還給central list,可以將kMaxOverages改小一點,比如1

還可以定期讓tcmalloc歸還空間給OS,

#include "google/malloc_extension.h"
  
MallocExtension::instance()->ReleaseFreeMemory();

實驗結果證明,tcmalloc分配速度的確快,而且程序不再像以前那樣內存只增不減。

上面Sanjay的文章已經對tcmalloc做了個大概的介紹,我看了一下tcmalloc的核心code,下面將其分配和釋放的過程簡單介紹一下:

線程申請資源:

1. 首先根據申請空間的大小從當前線程的可用內存塊裏面找(每個進程維護一組鏈表,每個鏈表代表一定大小的可用空間)

2. 如果step 1沒有找到,則到central list裏面查找(central list跟線程各自維護的list結構很像,爲不同的size各自維護一組可用空間列表)

3. 如果step 2 central list也沒有找到,則計算分配size個字節需要分配多少page(變量:class_to_pages)

4. 根據pagemap查找page對應的可用的span列表,如果找到了,則直接返回span,central list會將該span切割成合適的大小放入對應的列表中,然後交給thread cache

5. 如果step 4沒有找到可用的span,則向OS直接申請,然後步驟同step 4。

注意的是tcmalloc向系統申請空間有三種方式:sbrk,mmap,/dev/mem文件,默認是三種都try的,一種不行換另外一種。

線程釋放資源:

1. 釋放某個object

2. 找到該object所在的span

3. 如果該span中所有object都被釋放,則釋放該span到對應的可用列表,在釋放的過程中,嘗試將該span跟左右spans merge成更大的span

4. 如果當前thread cache的free 空間大於指定預置,歸還部分空間給central list

5. central list也會試圖通過釋放可用span列表的最後幾個span來將不用的空間歸還給OS

tcmalloc向OS申請/釋放資源是以span爲單位的。

tcmalloc裏面不少實現值得稱道,比如pagesize到void*的mapping方式,添加/移除鏈表元素的時候利用結構體內存佈局直接賦值,span/page/item的內存層次結構等,值得一看。

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