來自: 大熊先生_博客園 鏈接:http://www.cnblogs.com/Creator/archive/2012/04/05/2433386.html(點(diǎn)擊尾部閱讀原文前往)
問題:
我們?cè)趯懗绦虻臅r(shí)候經(jīng)常發(fā)現(xiàn)程序使用的內(nèi)存往往比我們申請(qǐng)的多,為了優(yōu)化程序的內(nèi)存占用,攪盡腦汁想要優(yōu)化內(nèi)存占用,可是發(fā)現(xiàn)自己的代碼也無從優(yōu)化了,怎么辦?現(xiàn)在我們把我們的焦點(diǎn)放到malloc上,畢竟我們向系統(tǒng)申請(qǐng)的內(nèi)存都是通過它完成了,不了解他,也就不能徹底的優(yōu)化內(nèi)存占用。
來個(gè)小例子 //g++ -o malloc_addr_vec mallc_addr_vec.cpp 編譯 #include using namespace std; int main(int argc, char *argv[]) { int malloc_size = atoi(argv[1]); char * malloc_char; for (size_t i = 0; i <>1024*1024; ++i) { malloc_char = new char[malloc_size]; } while (1) {}//此時(shí)查看內(nèi)存占用 return 0; }
本文的測(cè)試環(huán)境為Linux 64Bit ,使用G++編譯為可執(zhí)行文件后,使用不同的啟動(dòng)參數(shù)啟動(dòng),使用top命令查看程序占用的內(nèi)存,這里我們主要是看RES指標(biāo)
RES -- Resident size (kb)
The non-swapped physical memory a task has used.
測(cè)試案例:
1、每次new 1 Byte Do 1024*1024次
./malloc_addr_vec 1
啟動(dòng)程序后的內(nèi)存占用
內(nèi)存消耗 32MB
2、每次new 24 Byte Do 1024*1024次
./malloc_addr_vec 24
啟動(dòng)程序后的內(nèi)存占用
內(nèi)存消耗32MB
3、每次new 25 Byte Do 1024*1024次
/malloc_addr_vec 25
啟動(dòng)程序后的內(nèi)存占用
內(nèi)存消耗48MB
為什么我們每次new 1Byte 和每次 new 24Byte系統(tǒng)消耗的內(nèi)存一樣呢?,為什么每次new 25Byte和 每次new 24Byte占用的內(nèi)存完全不同呢?
不知道大家在寫程序的時(shí)候有沒有關(guān)注過這個(gè)問題。我一次遇到時(shí),吐槽一句:What the fuck malloc.
原因分析:
在大多數(shù)情況下,編譯器和C庫透明地幫你處理對(duì)齊問題。POSIX 標(biāo)明了通過malloc( ), calloc( ), 和 realloc( ) 返回的地址對(duì)于任何的C類型來說都是對(duì)齊的。
對(duì)齊參數(shù)(MALLOC_ALIGNMENT) 大小的設(shè)定并需滿足兩個(gè)特性
1、必須是2的冪 2、必須是(void *)的整數(shù)倍
至于為什么會(huì)要求是(void *)的整數(shù)倍,這個(gè)目前我還不太清楚,等你來發(fā)現(xiàn)...
根據(jù)這個(gè)原理,在32位和64位的對(duì)齊單位分別為8字節(jié)和16字節(jié)
但是這并解釋不了上面的測(cè)試結(jié)果,這是因?yàn)橄到y(tǒng)malloc分配的最小單位(MINSIZE)并不是對(duì)齊單位
為了進(jìn)一步了解細(xì)節(jié),從GNU網(wǎng)站中把glibc源碼下載下來,查看其 malloc.c文件
#ifndef INTERNAL_SIZE_T #define INTERNAL_SIZE_T size_t #endif #define SIZE_SZ (sizeof(INTERNAL_SIZE_T)) #ifndef MALLOC_ALIGNMENT #define MALLOC_ALIGNMENT (2 * SIZE_SZ) #endif struct malloc_chunk { INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */ INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */ struct malloc_chunk* fd; /* double links -- used only if free. */ struct malloc_chunk* bk; }; An allocated chunk looks like this: chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of previous chunk, if allocated | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of chunk, in bytes |M|P| mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | User data starts here... . . . . (malloc_usable_size() bytes) . . | nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of chunk | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ #define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1) #define MIN_CHUNK_SIZE (sizeof(struct malloc_chunk)) #define MINSIZE / (unsigned long)(((MIN_CHUNK_SIZE+MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)) /* pad request bytes into a usable size -- internal version */
#define request2size(req) / (((req) + SIZE_SZ + MALLOC_ALIGN_MASK < minsize)="" ?="" ="" ="" ="" ="" ="" ="" ="" ="" ="" ="" ="" minsize="" :="" ="" ="" ="" ="" ="" ="" ="" ="" ="" ="" ="" ="" ="" ="" ="" ="" ="" ="" ="" ="" ="" ="" ="" ="" ="" ="" /="" ="" ="" ="" ="" ="" ((req)="" +="" size_sz="" +="" malloc_align_mask)="" &="">
其中request2size這個(gè)宏就是glibc的內(nèi)存對(duì)齊操作,MINSIZE就是使用malloc時(shí)占用內(nèi)存的最小單位。根據(jù)宏定義可推算在32位系統(tǒng)中MINSIZE為16字節(jié),在64位系統(tǒng)中MINSIZE一般為32字節(jié)。從request2size還可以知道,如果是64位系統(tǒng),申請(qǐng)內(nèi)存為1~24字節(jié)時(shí),系統(tǒng)內(nèi)存消耗32字節(jié),當(dāng)申請(qǐng)內(nèi)存為25字節(jié)時(shí),系統(tǒng)內(nèi)存消耗48字節(jié)。 如果是32位系統(tǒng),申請(qǐng)內(nèi)存為1~12字節(jié)時(shí),系統(tǒng)內(nèi)存消耗16字節(jié),當(dāng)申請(qǐng)內(nèi)存為13字節(jié)時(shí),系統(tǒng)內(nèi)存消耗24字節(jié)。
一般他們的差距是一個(gè)指針大小,計(jì)算公式是
max(MINSIZE,in_use_size)
其中in_use_size=(要求大小+2*指針大小-指針大小)align to MALLOC_ALIGNMENT
(對(duì)于上面計(jì)算的由來可以參見glibc 內(nèi)存池管理 ptmalloc這篇文章的第4節(jié)chuck部分以及搜一下malloc的內(nèi)部實(shí)現(xiàn)源碼 )
為了證明這個(gè)理論的正確性,我們需要計(jì)算一次malloc到底花掉了多少內(nèi)存,我們用如下代碼分別在32bit Linux和 64bit Linux上做測(cè)試 #include #include int main() { char * p1; char * p2; int i=1; printf('%d\n',sizeof(char *)); for(;i100;i++) { p1=NULL; p2=NULL; p1=(char *)malloc(i*sizeof(char)); p2=(char *)malloc(1*sizeof(char)); printf('i=%d %d\n',i,(p2-p1)); } getchar(); }
其測(cè)試結(jié)果如下:
32bit --------------------- Linux 32bit --------------------- 4 i=1 16 i=2 16 i=3 16 i=4 16 i=5 16 i=6 16 i=7 16 i=8 16 i=9 16 i=10 16 i=11 16 i=12 16 i=13 24 i=14 24 i=15 24 i=16 24 i=17 24 i=18 24 i=19 24 i=20 24 i=21 32 i=22 32 i=23 32 i=24 32 i=25 32 i=26 32 i=27 32 i=28 32 i=29 40 i=30 40 i=31 40 i=32 40 i=33 40 i=34 40 i=35 40 i=36 40 i=37 48 i=38 48 i=39 48 i=40 48 i=41 48 i=42 48 i=43 48 i=44 48 i=45 56 i=46 56 i=47 56 i=48 56 i=49 56 i=50 56 i=51 56 i=52 56 i=53 64 i=54 64 i=55 64 i=56 64 i=57 64 i=58 64 i=59 64 i=60 64 i=61 72 i=62 72 i=63 72 i=64 72 i=65 72 i=66 72 i=67 72 i=68 72 i=69 80 i=70 80 i=71 80 i=72 80 i=73 80 i=74 80 i=75 80 i=76 80 i=77 88 i=78 88 i=79 88 i=80 88 i=81 88 i=82 88 i=83 88 i=84 88 i=85 96 i=86 96 i=87 96 i=88 96 i=89 96 i=90 96 i=91 96 i=92 96 i=93 104 i=94 104 i=95 104 i=96 104 i=97 104 i=98 104 i=99 104
64bit
------------------- Linux 64bit ------------------- 8 i=1 32 i=2 32 i=3 32 i=4 32 i=5 32 i=6 32 i=7 32 i=8 32 i=9 32 i=10 32 i=11 32 i=12 32 i=13 32 i=14 32 i=15 32 i=16 32 i=17 32 i=18 32 i=19 32 i=20 32 i=21 32 i=22 32 i=23 32 i=24 32 i=25 48 i=26 48 i=27 48 i=28 48 i=29 48 i=30 48 i=31 48 i=32 48 i=33 48 i=34 48 i=35 48 i=36 48 i=37 48 i=38 48 i=39 48 i=40 48 i=41 64 i=42 64 i=43 64 i=44 64 i=45 64 i=46 64 i=47 64 i=48 64 i=49 64 i=50 64 i=51 64 i=52 64 i=53 64 i=54 64 i=55 64 i=56 64 i=57 80 i=58 80 i=59 80 i=60 80 i=61 80 i=62 80 i=63 80 i=64 80 i=65 80 i=66 80 i=67 80 i=68 80 i=69 80 i=70 80 i=71 80 i=72 80 i=73 96 i=74 96 i=75 96 i=76 96 i=77 96 i=78 96 i=79 96 i=80 96 i=81 96 i=82 96 i=83 96 i=84 96 i=85 96 i=86 96 i=87 96 i=88 96 i=89 112 i=90 112 i=91 112 i=92 112 i=93 112 i=94 112 i=95 112 i=96 112 i=97 112 i=98 112 i=99 112
了解了malloc的內(nèi)存對(duì)其原理后,對(duì)于程序的內(nèi)存占用的優(yōu)化又有了有的放矢。我們可以根據(jù)內(nèi)存對(duì)齊的原則來請(qǐng)求內(nèi)存,來制作我們的高效內(nèi)存池,從而避免隱形的資源浪費(fèi).
例如,目前STL的內(nèi)存池是以8Byte為對(duì)齊單位,內(nèi)存池free_list大小為
free_list[0] --------> 8 byte free_list[1] --------> 16 byte free_list[2] --------> 24 byte free_list[3] --------> 32 byte ... ... free_list[15] -------> 128 byte
STL內(nèi)存池在發(fā)現(xiàn)某個(gè)規(guī)則的內(nèi)存用完了時(shí),會(huì)進(jìn)行refill,在進(jìn)行chunk_alloc
例如8Byte大小的空間沒有了,調(diào)用refill,refill會(huì)將其空間準(zhǔn)備20個(gè),也就是20*8,當(dāng)然refill做不了內(nèi)存分配,他把20個(gè)8Byte的需求提交給chunk_alloc
chunk_alloc 能真正分配內(nèi)存,但是它分配的時(shí)候會(huì)將內(nèi)存空間*2,所以最終malloc的內(nèi)存為8*20*2=320 ,32bit系統(tǒng)給malloc的內(nèi)存為328,64bit系統(tǒng)給malloc的內(nèi)存為336
在32位和64位操作系統(tǒng)分別浪費(fèi)掉8Byte和16Byte,其實(shí)我們可以在chunk_alloc內(nèi)部簡(jiǎn)單的計(jì)算一下系統(tǒng)的內(nèi)存對(duì)齊,達(dá)到 chunk_alloc 級(jí)零浪費(fèi)...
至于 allocate級(jí)別的浪費(fèi),我想是避免不了了,譬如,我需要一個(gè)6Byte的空間,STL內(nèi)存池給我的確實(shí)8Byte
●本文編號(hào)82,以后想閱讀這篇文章直接輸入82即可。 ●輸入m可以獲取到文章目錄
推薦《15個(gè)技術(shù)類公眾微信》
|