为何使用malloc分配小于MMAP_THRESHOLD的内存时堆未发生变化?
小内存分配时堆大小未变化的原因
测试代码
int main(int argc, char ** argv) { char *p[10]; for(int i =0 ; i < 10; ++i) { ssize_t msize = pow(2,i) * sizeof(char); p[i] = (char*)malloc(msize); printf("%p\n",sbrk(0)); } printf("malloc done!\n"); for(int i =0 ;i < 10; ++i) { free(p[i]); } exit(0); }
运行输出
0x55ffd4dd5000 0x55ffd4dd5000 0x55ffd4dd5000 0x55ffd4dd5000 0x55ffd4dd5000 0x55ffd4dd5000 0x55ffd4dd5000 0x55ffd4dd5000 0x55ffd4dd5000 0x55ffd4dd5000 malloc done!
原因分析
glibc的malloc不会每次小内存分配都调用sbrk()/brk()调整堆大小,核心原因是它的预分配与内存复用机制:
- 首次调用malloc分配小内存时,malloc会一次性通过
sbrk()申请一块远大于当前需求的堆内存区域(也就是arena,默认大小通常在几十KB级别,比如128KB),后续的小内存分配都直接从这块预分配区域里拆分,不会触发新的堆扩展操作。 - 你的测试里,每次分配的内存最大才512字节,10次分配总大小也才1023字节,远小于预分配的堆空间,所以所有分配都在初始预分配的区域内完成,堆顶地址(
sbrk(0)的返回值)自然不会变化。 - 只有当预分配的堆空间被耗尽,或者后续分配的内存块大小超过剩余空闲空间时,malloc才会再次调用
sbrk()扩展堆。
另外补充:即使调用free()释放内存,malloc也不会立刻调用sbrk()收缩堆,而是把释放的内存加入空闲链表,留待后续分配复用——这是为了减少系统调用的开销,毕竟系统调用的成本远高于用户态的内存管理操作。
内容的提问来源于stack exchange,提问作者CodeFish
相关产品推荐
相关产品推荐

