動機
TCMalloc要比glibc 2.3的malloc(可以從一個叫作ptmalloc2的獨立庫獲得)和其他我測試過的malloc都快。ptmalloc在一台2.8GHz的P4機 器上(對於小對象)執行一次malloc及free大約需要300納秒。而TCMalloc的版本同樣的操作大約只需要50納秒。malloc版本的速度 是至關重要的,因為如果malloc不夠快,應用程序的作者就很有可能在malloc之上寫一個自己的自由列表。這就可能導致額外的代碼復雜度,以及更多 的內存占用――除非作者本身非常仔細地劃分自由列表的大小並經常從自由列表中清除空閒的對象。
TCMalloc也減少了多線程程序中的鎖爭用情況。對於小對象,幾乎已經達到了零爭用。對於大對象,TCMalloc嘗試使用粒度較好和有效的自旋鎖。 ptmalloc同樣是通過使用每線程各自的場地來減少鎖爭用,但是ptmalloc2使用每線程場地有一個很大的問題。在ptmalloc2中,內存可 能會從一個場地移動到另一個。這有可能導致大量空間被浪費。例如,在一個Google的應用中,第一階段可能會為其URL標准化的數據結構分配大約 300MB內存。當第一階段結束後,第二階段將從同樣的地址空間開始。如果第二個階段被安排到了一個與第一階段什?用的場地不同的場地,這個階段不會復用 任何第一階段留下的的內存,並會給地址空間添加另外一個300MB。類似的內存爆炸問題也可以在其他的應用中看到。
TCMalloc的另一個好處是小對象的空間最優表現形式。例如,分配N個8字節對象可能要使用大約8N * 1.01字節的空間。即,多用百分之一的空間。而ptmalloc2中每個對象都使用了一個四字節的頭,(我認為)並將最終的尺寸規整為8字節的倍數,最 後使用了16N字節。
使用
要使用TCMalloc,只要將tcmalloc通過“-ltcmalloc”鏈接器標志接入你的應用即可。
你也可以通過使用LD_PRELOAD在不是你自己編譯的應用中使用tcmalloc:
$ LD_PRELOAD="/usr/lib/libtcmalloc.so"
LD_PRELOAD比較討巧,我們也不十分推薦這種用法。
TCMalloc還包含了一個堆檢查器以及一個堆測量器。
如果你更想鏈接不包含堆測量器和檢查器的TCMalloc版本(比如可能為了減少靜態二進制文件的大小),你可以接入libtcmalloc_minimal
。
概覽
TCMalloc給每個線程分配了一個線程局部緩存。小分配可以直接由線程局部緩存來滿足。需要的話,會將對象從中央數據結構移動到線程局部緩存中,同時定期的垃圾收集將用於把內存從線程局部緩存遷移回中央數據結構中。
TCMalloc將尺寸小於<=
32K的對象(“小”對象)和大對象區分開來。大對象直接使用頁級分配器(一個頁是一個4K的對齊內存區域)從中央堆直接分配。即,一個大對象總是頁對齊的並占據了整數個數的頁。
連續的一些頁面可以被分割為一系列小對象,而他們的大小都相同。例如,一個連續的頁面(4K)可以被劃分為32個128字節的對象。
小對象的分配
每個小對象的大小都會被映射到170個可分配的尺寸類別中的一個。例如,在分配961到1024字節時,都會歸整為1024字節。尺寸類別這樣隔 開:較小的尺寸相差8字節,較大的尺寸相差16字節,再大一點的尺寸差32字節,如此類推。最大的間隔(對於尺寸 >= ~2K的)是256字節。
一個線程緩存對每個尺寸類都包含了一個自由對象的單向鏈表。
當分配一個小對象時:
如果自由列表為空:
如果中央自由列表也為空:(1) 我們從中央頁分配器分配了一連串頁面。(2) 將他們分割成該尺寸類的一系列對象。(4) 像前面一樣,將部分對象移入線程局部的自由列表中。
大對象的分配
一個大對象的尺寸(> 32K)會被除以一個頁面尺寸(4K)並取整(大於結果的最小整數),同時是由中央頁面堆來處理的。中央頁面堆又是一個自由列表的陣列。對於i < 256
而言,第k
個條目是一個由k
個頁面組成的自由列表。第256
個條目則是一個包含了長度>= 256
個頁面的自由列表:
k個頁面的一次分配通過在第k個自由列表中查找來完成。如果該自由列表為空,那麼我們則在下一個自由列表中查找,如此繼續。最終,如果必要的話,我們將在 最 後一個自由列表中查找。如果這個動作也失敗了,我們將向系統獲取內存(使用sbrk、mmap或者通過在/dev/mem中進行映射)。
如果k個頁面的一次分配行為由連續的長度> k的頁面滿足了,剩下的連續頁面將被重新插回到頁面堆的對應的自由列表中。
更多詳情見請繼續閱讀下一頁的精彩內容: http://www.linuxidc.com/Linux/2014-07/104511p2.htm