理会操纵系统的内存分派(malloc)对齐计策
副标题#e#
问题:
我们在写措施的时候常常发明措施利用的内存往往比我们申请的多,为了优化措施的内存占用,搅尽脑汁想要优化内存占用,但是发明本身的代码也无从优化了,怎么办?此刻我们把我们的核心放到malloc上,究竟我们向系统申请的内存都是通过它完成了,不相识他,也就不能彻底的优化内存占用。
来个小例子
//g++ -o malloc_addr_vec mallc_addr_vec.cpp 编译 #include<iostream> 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) {}//此时查察内存占用 return 0; }
本文的测试情况为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.
#p#副标题#e#
原因阐明:
在大大都环境下,编译器和C库透明地帮你处理惩罚对齐问题。POSIX 标明白通过malloc( ), calloc( ), 和 realloc( ) 返回的地点对付任何的C范例来说都是对齐的。
对齐参数(MALLOC_ALIGNMENT) 巨细的设定并需满意两个特性
1.必需是2的幂
2.必需是(void *)的整数倍
至于为什么会要求是(void *)的整数倍,这个今朝我还不太清楚,等你来发明…
按照这个道理,在32位和64位的对齐单元别离为8字节和16字节
可是这并表明不了上面的测试功效,这是因为系统malloc分派的最小单元(MINSIZE)并不是对齐单元
为了进一步相识细节,从GNU网站中把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) & ~MALLOC_ALIGN_MASK)
#p#分页标题#e#
个中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上做测试
#include<stdio.h> #include<stdlib.h> int main() { char * p1; char * p2; int i=1; printf("%d\n",sizeof(char *)); for(;i<100;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(); }
其测试功效如下:
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的内存对其道理后,对付措施的内存占用的优化又有了有的放矢。我们可以按照内存对齐的原则来请求内存,来建造我们的高效内存池,从而制止隐形的资源挥霍.
譬喻,今朝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
STL内存池在发明某个法则的内存用完了时,会举办refill,在举办chunk_alloc
譬喻8Byte巨细的空间没有了,挪用refill,refill会将其空间筹备20个,也就是20*8,虽然refill做不了内存分派,他把20个8Byte的需求提交给chunk_alloc
chunk_alloc 能真正分派内存,可是它分派的时候会将内存空间*2,所以最终malloc的内存为8*20*2=320 ,32bit系统给malloc的内存为328,64bit系统给malloc的内存为336
在32位和64位操纵系统别离挥霍掉8Byte和16Byte,其实我们可以在chunk_alloc内部简朴的计较一下系统的内存对齐,到达 chunk_alloc 级零挥霍…
至于 allocate级此外挥霍,我想是制止不了了,譬如,我需要一个6Byte的空间,STL内存池给我简直实8Byte
作者:cnblogs 大熊(先生)