程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 探究OS的內存分配對齊策略

探究OS的內存分配對齊策略

編輯:C++入門知識

問題:
  我們在寫程序的時候經常發現程序使用的內存往往比我們申請的多,為了優化程序的內存占用,攪盡腦汁想要優化內存占用,可是發現自己的代碼也無從優化了,怎麼辦?現在我們把我們的焦點放到malloc上,畢竟我們向系統申請的內存都是通過它完成了,不了解他,也就不能徹底的優化內存占用。
來個小例子
//g++ -o malloc_addr_vec  mallc_addr_vec.cpp 編譯
 2 #include<iostream>
 3 using namespace std;
 4 int main(int argc, char *argv[])
 5 {
 6     int malloc_size = atoi(argv[1]);
 7     char * malloc_char;
 8     for (size_t i = 0; i < 1024*1024; ++i) {
 9         malloc_char = new char[malloc_size];
10     }
11     while (1) {}//此時查看內存占用
12     return 0;
13 }

 本文的測試環境為Linux 64Bit ,使用G++編譯為可執行文件後,使用不同的啟動參數啟動,使用top命令查看程序占用的內存,這裡我們主要是看RES指標
RES  --  Resident size (kb)
The non-swapped physical memory a task has used. 
 
測試案例:
1.每次new 1 Byte   Do 1024*1024次
 ./malloc_addr_vec 1
啟動程序後的內存占用
 
 \


內存消耗 32MB

 2.每次new 24 Byte  Do 1024*1024次

 ./malloc_addr_vec 24

 啟動程序後的內存占用

  \


 

內存消耗32MB

 3.每次new 25 Byte   Do 1024*1024次

 ./malloc_addr_vec 25

啟動程序後的內存占用  


 \

內存消耗48MB
 
  為什麼我們每次new 1Byte 和每次 new 24Byte系統消耗的內存一樣呢?,為什麼每次new 25Byte和 每次new 24Byte占用的內存完全不同呢?
  不知道大家在寫程序的時候有沒有關注過這個問題。我一次遇到時,吐槽一句:What the fuck malloc.

原因分析:
   在大多數情況下,編譯器和C庫透明地幫你處理對齊問題。POSIX 標明了通過malloc( ), calloc( ), 和 realloc( ) 返回的地址對於任何的C類型來說都是對齊的。
  對齊參數(MALLOC_ALIGNMENT) 大小的設定並需滿足兩個特性
 1.必須是2的冪
 2.必須是(void *)的整數倍
  至於為什麼會要求是(void *)的整數倍,這個目前我還不太清楚,等你來發現...
  根據這個原理,在32位和64位的對齊單位分別為8字節和16字節
  但是這並解釋不了上面的測試結果,這是因為系統malloc分配的最小單位(MINSIZE)並不是對齊單位
 為了進一步了解細節,從GNU網站中把glibc源碼下載下來,查看其 malloc.c文件
  View Code
 1 #ifndef INTERNAL_SIZE_T 
 2 #define INTERNAL_SIZE_T size_t 
 3 #endif 
 4 #define SIZE_SZ                (sizeof(INTERNAL_SIZE_T)) 
 5 #ifndef MALLOC_ALIGNMENT 
 6 #define MALLOC_ALIGNMENT       (2 * SIZE_SZ) 
 7 #endif 
 8
 9
10 struct malloc_chunk { 
11   INTERNAL_SIZE_T      prev_size;  /* Size of previous chunk (if free).  */ 
12   INTERNAL_SIZE_T      size;       /* Size in bytes, including overhead. */ 
13   struct malloc_chunk* fd;         /* double links -- used only if free. */ 
14   struct malloc_chunk* bk; 
15 }; 
16
17     An allocated chunk looks like this: 
18     chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
19             |             Size of previous chunk, if allocated            | | 
20             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
21             |             Size of chunk, in bytes                       |M|P| 
22       mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
23             |             User data starts here...                          . 
24             .                                                               . 
25             .             (malloc_usable_size() bytes)                      . 
26             .                                                               | 
27 nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
28             |             Size of chunk                                     | 
29             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
30            
31            
32 #define MALLOC_ALIGN_MASK      (MALLOC_ALIGNMENT - 1) 
33 #define MIN_CHUNK_SIZE        (sizeof(struct malloc_chunk)) 
34 #define MINSIZE  / 
35   (unsigned long)(((MIN_CHUNK_SIZE+MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)) 
36 /* pad request bytes into a usable size -- internal version */ 
37 #define request2size(req)                                         / 
38   (((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE)  ?             / 
39    MINSIZE :                                                      / 
40    ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)

   其中request2size這個宏就是glibc的內存對齊操作,MINSIZE就是使用malloc時占用內存的最小單位。根據宏定義可推算在32位系統中MINSIZE為16字節,在64位系統中MINSIZE一般為32字節。從request2size還可以知道,如果是64位系統,申請內存為1~24字節時,系統內存消耗32字節,當申請內存為25字節時,系統內存消耗48字節。 如果是32位系統,申請內存為1~12字節時,系統內存消耗16字節,當申請內存為13字節時,系統內存消耗24字節。
 
一般他們的差距是一個指針大小,計算公式是
max(MINSIZE,in_use_size)
其中in_use_size=(要求大小+2*指針大小-指針大小)align to MALLOC_ALIGNMENT
(對於上面計算的由來可以參見glibc 內存池管理 ptmalloc這篇文章的第4節chuck部分以及搜一下malloc的內部實現源碼 )
  為了證明這個理論的正確性,我們需要計算一次malloc到底花掉了多少內存,我們用如下代碼分別在32bit Linux和 64bit Linux上做測試
 2 #include<stdio.h>
 3 #include<stdlib.h>
 4 int main()
 5 {
 6         char * p1;
 7         char * p2;
 8         int i=1;
 9         printf("%d\n",sizeof(char *));
10         for(;i<100;i++)
11         {
12                 p1=NULL;
13                 p2=NULL;
14                 p1=(char *)malloc(i*sizeof(char));
15                 p2=(char *)malloc(1*sizeof(char));
16                 printf("i=%d     %d\n",i,(p2-p1));
17         }
18
19         getchar();
20 }

其測試結果如下:
32bit
  View Code
  1 ---------------------
  2 Linux  32bit
  3 ---------------------
  4 4
  5 i=1 16
  6 i=2 16
  7 i=3 16
  8 i=4 16
  9 i=5 16
 10 i=6 16
 11 i=7 16
 12 i=8 16
 13 i=9 16
 14 i=10 16
 15 i=11 16
 16 i=12 16
 17 i=13 24
 18 i=14 24
 19 i=15 24
 20 i=16 24
 21 i=17 24
 22 i=18 24
 23 i=19 24
 24 i=20 24
 25 i=21 32
 26 i=22 32
 27 i=23 32
 28 i=24 32
 29 i=25 32
 30 i=26 32
 31 i=27 32
 32 i=28 32
 33 i=29 40
 34 i=30 40
 35 i=31 40
 36 i=32 40
 37 i=33 40
 38 i=34 40
 39 i=35 40
 40 i=36 40
 41 i=37 48
 42 i=38 48
 43 i=39 48
 44 i=40 48
 45 i=41 48
 46 i=42 48
 47 i=43 48
 48 i=44 48
 49 i=45 56
 50 i=46 56
 51 i=47 56
 52 i=48 56
 53 i=49 56
 54 i=50 56
 55 i=51 56
 56 i=52 56
 57 i=53 64
 58 i=54 64
 59 i=55 64
 60 i=56 64
 61 i=57 64
 62 i=58 64
 63 i=59 64
 64 i=60 64
 65 i=61 72
 66 i=62 72
 67 i=63 72
 68 i=64 72
 69 i=65 72
 70 i=66 72
 71 i=67 72
 72 i=68 72
 73 i=69 80
 74 i=70 80
 75 i=71 80
 76 i=72 80
 77 i=73 80
 78 i=74 80
 79 i=75 80
 80 i=76 80
 81 i=77 88
 82 i=78 88
 83 i=79 88
 84 i=80 88
 85 i=81 88
 86 i=82 88
 87 i=83 88
 88 i=84 88
 89 i=85 96
 90 i=86 96
 91 i=87 96
 92 i=88 96
 93 i=89 96
 94 i=90 96
 95 i=91 96
 96 i=92 96
 97 i=93 104
 98 i=94 104
 99 i=95 104
100 i=96 104
101 i=97 104
102 i=98 104
103 i=99 104

 64bit
  View Code
  1 -------------------
  2 Linux  64bit
  3 -------------------
  4 8
  5 i=1 32
  6 i=2 32
  7 i=3 32
  8 i=4 32
  9 i=5 32
 10 i=6 32
 11 i=7 32
 12 i=8 32
 13 i=9 32
 14 i=10 32
 15 i=11 32
 16 i=12 32
 17 i=13 32
 18 i=14 32
 19 i=15 32
 20 i=16 32
 21 i=17 32
 22 i=18 32
 23 i=19 32
 24 i=20 32
 25 i=21 32
 26 i=22 32
 27 i=23 32
 28 i=24 32
 29 i=25 48
 30 i=26 48
 31 i=27 48
 32 i=28 48
 33 i=29 48
 34 i=30 48
 35 i=31 48
 36 i=32 48
 37 i=33 48
 38 i=34 48
 39 i=35 48
 40 i=36 48
 41 i=37 48
 42 i=38 48
 43 i=39 48
 44 i=40 48
 45 i=41 64
 46 i=42 64
 47 i=43 64
 48 i=44 64
 49 i=45 64
 50 i=46 64
 51 i=47 64
 52 i=48 64
 53 i=49 64
 54 i=50 64
 55 i=51 64
 56 i=52 64
 57 i=53 64
 58 i=54 64
 59 i=55 64
 60 i=56 64
 61 i=57 80
 62 i=58 80
 63 i=59 80
 64 i=60 80
 65 i=61 80
 66 i=62 80
 67 i=63 80
 68 i=64 80
 69 i=65 80
 70 i=66 80
 71 i=67 80
 72 i=68 80
 73 i=69 80
 74 i=70 80
 75 i=71 80
 76 i=72 80
 77 i=73 96
 78 i=74 96
 79 i=75 96
 80 i=76 96
 81 i=77 96
 82 i=78 96
 83 i=79 96
 84 i=80 96
 85 i=81 96
 86 i=82 96
 87 i=83 96
 88 i=84 96
 89 i=85 96
 90 i=86 96
 91 i=87 96
 92 i=88 96
 93 i=89 112
 94 i=90 112
 95 i=91 112
 96 i=92 112
 97 i=93 112
 98 i=94 112
 99 i=95 112
100 i=96 112
101 i=97 112
102 i=98 112
103 i=99 112

 
了解了malloc的內存對其原理後,對於程序的內存占用的優化又有了有的放矢。我們可以根據內存對齊的原則來請求內存,來制作我們的高效內存池,從而避免隱形的資源浪費.
例如,目前STL的內存池是以8Byte為對齊單位,內存池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
我們可以將其優化為
32bit  OS 16-4+n*8
64bit  OS 32-8+n*16
n=(0,1,2,3.....max)
這樣,占用同樣大小的內存池,可用性會更高...
 

摘自  大熊 | Zealot Yin
 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved