週末抽空看了一下tcmalloc,瞭解了個大概。下面記錄一下。linux
tcmalloc就是一個內存分配器,管理堆內存,主要影響malloc和free,用於下降頻繁分配、釋放內存形成的性能損耗,而且有效地控制內存碎片。glibc中的內存分配器是ptmalloc2,tcmalloc號稱要比它快。一次malloc和free操做,ptmalloc須要300ns,而tcmalloc只要50ns。同時tcmalloc也優化了小對象的存儲,須要更少的空間。tcmalloc特別對多線程作了優化,對於小對象的分配基本上是不存在鎖競爭,而大對象使用了細粒度、高效的自旋鎖(spinlock)。分配給線程的本地緩存,在長時間空閒的狀況下會被回收,供其餘線程使用,這樣提升了在多線程狀況下的內存利用率,不會浪費內存,而這一點ptmalloc2是作不到的。編程
tcmalloc區別的對待大、小對象。它爲每一個線程分配了一個線程局部的cache,線程須要的小對象都是在其cache中分配的,因爲是thread local的,因此基本上是無鎖操做(在cache不夠,須要增長內存時,會加鎖)。同時,tcmalloc維護了進程級別的cache,全部的大對象都在這個cache中分配,因爲多個線程的大對象的分配都從這個cache進行,因此必須加鎖訪問。在實際的程序中,小對象分配的頻率要遠遠高於大對象,經過這種方式(小對象無鎖分配,大對象加鎖分配)能夠提高總體性能。數組
線程級別cache和進程級別cache實際上就是一個多級的空閒塊列表(Free List)。一個Free List以大小爲k bytes倍數的空閒塊進行分配,包含n個鏈表,每一個鏈表存放大小爲nk bytes的空閒塊。在tcmalloc中,<=32KB的對象被稱做是小對象,>32KB的是大對象。在小對象中,<=1024bytes的對象以8n bytes分配,1025<size<=32KB的對象以128n bytes大小分配,好比:要分配20bytes則返回的空閒塊大小是24bytes的,這樣在<=1024的狀況下最多浪費7bytes,>1025則浪費127bytes。而大對象是以頁大小4KB進行對齊的,最多會浪費4KB - 1 bytes。下圖就是一個基本的free list的示意圖:緩存
實際上,一個free list(我稱之爲空閒塊列表)就是一個數組索引多個鏈表,每一個鏈表存放相同大小的塊。能夠根據要分配的內存大小size算出合適的塊在free list中的下標,而後找到對應的空閒塊鏈表。數據結構
tcmalloc的數據結構組織以下:多線程
Thread-local free list:線程本地的空閒塊cache,用於分配小對象。性能
Heap free list:中心free list,全局惟一,用於按頁對齊分配大對象或者是將連續的多個頁(被稱做span)分割成多個小對象的空閒塊分配給thread-local free list。優化
Page array:用於描述當前tcmalloc持有的內存狀態,完成的是從page number到span的映射。spa
下面看一下小對象的分配:操作系統
(1)根據分配的size計算出對應的空閒塊大小,從而肯定對應空閒塊鏈表,而後從thread local的free list進行分配。
(2)若是的空閒塊鏈表非空,直接將頭結點對應的空閒塊返回並從空閒塊鏈表中將其刪除。
(3)若是空閒塊鏈表是空的,須要從heap free list獲取一個span。若是heap free list非空,則將span切分紅多個相同大小的空閒塊插入空閒塊鏈表中,而後返回頭結點。
(4)若是heap free list是空的,則調用sbrk或者mmap進行內存的分配一系列連續的內存頁,做爲span,而後切分紅多個相同大小的空閒塊插入空閒塊鏈表,而後返回頭結點。
大對象的分配就要簡單多了,直接從heap free list分配4nKB大小的空閒塊便可,若是heap free list不存在該大小的空閒塊,經過系統調用分配連續的內存頁。
tcmalloc還會對thread local cache進行垃圾收集,從而避免內存浪費。
tcmalloc屬於gperftools,安裝比較簡單,須要注意一下,在64bit系統上須要先安裝libunwind(http://download.savannah.gnu.org/releases/libunwind/libunwind-0.99-beta.tar.gz,只能是這個版本),這個庫爲基於64位CPU和操做系統的程序提供了基本的堆棧展轉開解功能,其中包括用於輸出堆棧跟蹤的API、用於以編程方式展轉開解堆棧的API以及支持C++異常處理機制的API,32bit系統不需安裝。在安裝過程當中,可能出現下列錯誤:
gcc -DHAVE_CONFIG_H -I. -I../include -I../include -I../include/tdep-x86_64 -I. -D_GNU_SOURCE -DNDEBUG -g -O2 -fexceptions -Wall -Wsign-compare -MT setjmp/longjmp.lo -MD -MP -MF setjmp/.deps/longjmp.Tpo -c setjmp/longjmp.c -fPIC -DPIC -o setjmp/.libs/longjmp.o /usr/include/x86_64-linux-gnu/bits/setjmp2.h:26:13: error: 'longjmp' aliased to undefined symbol '_longjmp
是由於缺乏編譯選項U_FORTIFY_SOURCE,解決方法有兩種:
1,還沒有調用configure進行配置,則執行CPPFLAGS=-U_FORTIFY_SOURCE ./configure ... 會自動將編譯選項添加到Makefile中。
2,已經配置過,直接修改Makefile,查找CPPFLAGS而後添加上-U_FORTIFY_SOURCE。
而後就是安裝gperftools,這個正常安裝便可,默認安裝到/usr/local/lib下,完成後調用lddconfig添加到動態連接庫緩存,而後就可使用了。
使用方法,很簡單,在編譯時加上tcmalloc動態連接庫便可
g++ test.cpp -ltcmalloc
源碼不需任何修改,tcmalloc會自動替換掉glibc默認的malloc和free,簡簡單單的一條命令就能夠提高很多性能,very good。