You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 09:15:31