TCMalloc 简介
昨天聊的64MB內存中,有使用TCMalloc,但是對其原理不是很了解,所以學習了下。
前言
先不使用TCMalloc,先使用glibc自帶的內存分配ptmalloc2進行內存分配,先看一個簡單的程序:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>int main( int argc, char *argv[] )
{malloc(1);getchar();
}
編譯運行:
gcc -g m.c -o m
./m
查看下我們申請一個字節的內存占用情況:
[root@izbp14xswj2tx6qgnz9dllz ~]# cat /proc/2888/maps|grep heap
00ed3000-00ef4000 rw-p 00000000 00:00 0                                  [heap]
[root@izbp14xswj2tx6qgnz9dllz ~]# python
Python 2.7.5 (default, Aug  4 2017, 00:39:18)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 0x00ef4000-0x00ed3000
135168
>>> 135168/1024
132
結論我們申請1個字節的內存,實際內存分配器給我們分配了132KB的內存,這樣做出是下次再申請內存的時候,如果小于132KB內存,不用再向系統申請,加快內存分配速度。即時我們把代碼改成如下:
char * p = (char*)  malloc(1);
free(p);
申請的內存仍然不釋放:
[root@izbp14xswj2tx6qgnz9dllz ~]# cat /proc/3248/maps|grep heap
022ce000-022ef000 rw-p 00000000 00:00 0                                  [heap]
[root@izbp14xswj2tx6qgnz9dllz ~]# python
Python 2.7.5 (default, Aug  4 2017, 00:39:18)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> (0x022ef000-0x022ce000)/1024
132
一 ?簡介
TCMalloc 庫全稱Thread-Caching Malloc 是谷歌開源工具google-perftools中的成員,是一個內存分配器,這個內存分配器與我們上面說的linux系統下默認的glibc中的ptmalloc2內存分配器有什么不同那。主要優點:
- 
tcmalloc 一次malloc或free操作更快,據說ptmalloc2需要300ns,tcmalloc 需要50ns。
 - 
tcmalloc 優化小對象的存儲,需要更少的空間。
 - 
tcmalloc 對多線程分配內存,小對象基本不存在鎖競爭,因為分配器會給現場分配本地緩存,長期空閑的情況下也不會被回收。
 
二 ?分配策略
在TCMalloc 對于不同申請內存的大小采用不同的策略,內存大小劃分如下:
- 
(0,256KB] 屬于小內存;
 - 
(256KB,1MB] 屬于中等內存;
 - 
(1M, +∞] 屬于大內存。在TCMalloc 中內存分配粒度有兩種一種是Object,大小是預設的規則,比如8字節,16字節,32字節,48字節等;另一種叫Span,一個Span是由多個Page組成,Object 是由Span切出來的。
 
2.1 ?Thread Cache
對于小內存分配,是在Thread Cache中分配的,為了減少內存碎片,TCMalloc按大小劃分了85個類別,這些稱為Size Class,每個Size Class都對應固定字節的大小,從8個字節,到16個字節...一直到256K,這些 Thread Cache采用雙向鏈表形式串聯起來,由于Thread Cache 是每個線程都有的,所以多線程同時分配內存的時候,如果Thread Cache 對應的Size Class中相應大小是空閑,就不用加鎖,比如我們申請15B內存,這個線程的Thread Cache中從Size Class中的16字節對應的free list中分配內存。
Thread Cache 在整個分配器中是個雙向鏈表,每個都對應一個空閑內存池。每個線程的Thread Cache大小限制最大4MB,大小在512KB-4MB之間。所有線程Thread Cache的總大小默認限制為32MB,取值范圍從512KB到1GB之間。
2.2 Central Cache
Central Cache是所有線程公用的緩存,Central Cache對于每個Size Class 都由一個獨立的鏈表來緩存空閑對象,當Thread Cache中內存不夠的時候,從中獲取,由于是所有線程公用的,所以從中取或歸還對象的時候,需要加鎖的,一般Thread Cache一次獲取或歸還多個空閑對象。
2.3 Page Heap
如果Central Cache的內存不夠的話,會向Page Heap申請。申請好的內存,將其拆分成一系列空閑對象,添加對應size class對應的free list中。Page Heap的內存結構是以span為單位組織的,每個span由不同的page 組成,Page Heap按照大小分為1page 對應span,2個pages對應span,一直到128個pages 對應的span,即128*8KB= 1024KB即1M,注意這里面的page為8KB(可以調整默認為8KB)。大小超過1M的,即超過128個pages的span存儲在有序的set中,如下圖:
2.4 小對象的分配策略
- 
將要分配的內存向上取到對應的size class對應的隊列中。
 - 
如果該線程的Thread Cache 對應的size class的free list 非空,取出一個空閑對象 返回。
 - 
如果該線程對應的size class的free list為空,則向 Central Cache申請內存:
 - 
- 
則向page heap 申請一個span (page heap不夠,則向系統申請內存)。
 - 
將span拆分成若干個size class對象。
 - 
如果 Central Cache對應的size class 有空閑對象,則取一些空閑對象。
 - 
如果Central Cache對應的size class 中也無空閑對象:
 
 - 
 - 
將申請到的size class 放到Thread Cache 的空閑隊列中,返回一個空閑對象。
 
2.5 中間對象的分配策略
即大于256KB 小于1MB內存的分配,分配的內存是直接從 page heap上選取與申請內存向上對應的span,比如申請內存3.5個pages,則直接從 4個pages 對應的span查找。假設向上對應的為k個pages:
- 
在 k pages 對應的span空閑隊列查找,如果沒有就向下找 k+1 個pages對應的span。
 - 
直到找到一個非空的n個pages對應的空閑span或者找到了128個pages對應的span
 - 
- 
從這個找到的n個pages對應的span中切出 k個pages 作為申請內存結果返回。
 - 
將剩下的n-k個pages 對應的span插入到相應的空閑隊列中。
 
 - 
 - 
如果找不到合適的span,則算大對象。
 
2.6 大對象的內存分配策略
同樣是在page heap中分配,由于申請的內存大于128個pages,所以需要在page heap的set里面去找合適的n個pages對應的span。
- 
如果找到,拆分:
 - 
- 
一個span大小為k 個page,返回。
 - 
剩下n- k個pages,如果n-k> 128,仍然放在set中,小于則放在對應的小span中。
 
 - 
 - 
如果找不到,則需要向系統申請內存,申請內存以span為單位。
 
2.7 內存回收
當應用程序釋放內存給Thread Cache,會回收到free list中,如果free list中內存達到一定程度后,會返回給Central Cache中,如果一個span中的所有對象都回收了,則再將span返還給page heap, page heap在合適時機返回給系統。
三 C 程序使用TCMalloc
編譯鏈接下tcmalloc庫,就可以了,通過下面的方法驗證下:
[root@localhost testcode]# g++ -g -ltcmalloc m.c -o m
[root@localhost testcode]# ./m
^C
[root@localhost testcode]# vim m.c
[root@localhost testcode]# gdb ./m
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-100.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/miaohq/testcode/m...done.
(gdb) b 7
Breakpoint 1 at 0x40071c: file m.c, line 7.
(gdb) r
Starting program: /home/miaohq/testcode/./m
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib64/libthread_db.so.1".Breakpoint 1, main (argc=1, argv=0x7fffffffe118) at m.c:7
7	    malloc(1);
Missing separate debuginfos, use: debuginfo-install glibc-2.17-196.el7_4.2.x86_64 libgcc-4.8.5-39.el7.x86_64 libstdc++-4.8.5-39.el7.x86_64
(gdb) s
tc_malloc (size=1) at src/tcmalloc.cc:1892
四 詩詞欣賞
八聲甘州·自王家無怨住襄城[宋]?[魏了翁]自王家無怨住襄城,世總生賢。
似謝階蘭玉,馬庭梧竹,一一堪憐。
富貴關人何事,且問此何緣。又
踏前朝腳,領蜀山川。
點檢重關復閣,尚甘棠匝地,喬木參天。
中興規畫,父老至今傳。
六十年、山河未改,只芳菲、不斷緊相聯。
相將又,參陪宰席,還似當年。
                            總結
以上是生活随笔為你收集整理的TCMalloc 简介的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 打流工具trex使用
 - 下一篇: 建设者还是二传手?