Cnew、堆分配与brk/mmap结论概要单次operator new/malloc往往在用户态由分配器缓存完成不必然触发内核态切换仅当分配器需要向操作系统索取新的虚拟内存页如brk/mmap等路径时才会通过系统调用陷入内核。具体分支依赖libc 版本、分配器实现ptmalloc / jemalloc / tcmalloc与运行时参数。目录new与用户态 / 内核态语言层面的 new 形态brk/sbrk与mmap对比进程虚拟地址布局概念glibcmalloc决策示意实测观察brk与mmap路径现代分配器的线程缓存tcmalloc / jemalloc观测与调优线索参考链接new与用户态 / 内核态概念说明用户态应用程序常态执行不能直接操作部分硬件与内核数据结构。内核态内核执行路径通过系统调用、异常、中断从用户态进入。典型new T流程非 placement、非定制operator new时实现常基于malloc或等价堆分配分配存储向堆分配器申请一块足以存放T的内存。构造在已分配存储上调用T的构造函数。若分配器在已有 arena / 堆顶 / 线程缓存内即可满足请求则整个过程可在用户态完成若需扩大堆顶或新建匿名映射则发生系统调用如brk、mmap此时发生用户态 → 内核态切换。内核态经系统调用用户态命中缓存需向 OS 要内存new T / malloc分配器快速路径arena / tcache / binsbrk / mmap建立或扩展映射返回指针概念说明虚拟地址已映射 ≠ 立刻占满物理内存常为按需缺页fault时再配物理页观测 RSS 与commit策略因 OS 而异多次小new往往只触达分配器数据结构偶尔才推进堆顶或mmap语言层面的 new 形态形式与brk/mmap的关系概括new T默认通常调operator new→ 多实现转malloc后续讨论同本文new (p) Tplacement不申请堆内存仅在已有存储上构造new T[n]/new (std::align_val_t) T可能走对齐或数组版operator new仍多基于malloc家族std::bad_alloc分配失败时抛出除非使用nothrow newstd::set_new_handler失败时可重试、记录日志或abort不改变「是否进内核」的本质delete/free小块常回到分配器空闲链表未必立刻munmap或收缩brk故进程VSS可能长期高于「当前存活对象」直觉。brk/sbrk与mmap对比维度brk/sbrk堆顶mmap匿名映射作用位置调整进程堆末端program break在虚拟地址空间中独立映射一段区域与堆连续性与既有堆连续延伸一般为独立映射不与堆顶连续典型粒度分配器内部再切分向 OS 要页时常按页对齐至少一页如 4KiB远小于一页的请求会浪费页内空间释放归还 OS堆顶难以随free立即收缩常由分配器策略决定munmap可较直接解除映射碎片与锁长期小块分配易加剧堆碎片多线程共享堆顶时需同步大块独立映射有利于隔离小块频繁mmap/munmap系统调用与 TLB 成本高常见用途传统malloc中小块扩展堆大块、独立生命周期、posix_memalign部分路径、内存映射文件等sbrk在可移植代码中已较少直接使用Linux上仍以brk为调整堆顶的系统调用语义核心。实际虚拟内存布局因内核、ASLR、mmap映射顺序而异。进程虚拟地址布局概念低地址 ─────────────────────────────────────────────► 高地址 | 文本 / 只读数据 等 | … | 堆 ──► brk堆顶| 间隙 | mmap 映射区库、大块堆、匿名映射… |高地址侧常见区域mmap 映射libc / 栈 / 大块分配 …低地址侧代码与常量等已初始化数据 / BSS堆brk 向上增长注意上图仅为教学示意PIE/ASLR会使各段基址每次运行变化且栈增长方向与mmap落点依平台与内核策略而定。Linux 下可用cat /proc/self/maps或pmap对照本进程真实布局。glibcmalloc决策示意以下为glibc ptmalloc常见教材式简化MMAP_THRESHOLD、ARENA_MAX等可用mallopt或环境/版本调整不同 glibc 版本默认值可能不同。通常 yes是否通常 no 大块malloc(size) / 典型 operator newsize 小于 mmap 阈值tcache / fastbin / smallbin 等能否在现有堆满足用户态返回指针brk 等扩展堆mmap 匿名映射小块路径尽量在线程缓存 / 堆中完成减少对brk/mmap的调用频率。大块路径直接mmapfree时可munmap减轻堆碎片与长期占用。实测观察brk与mmap路径下面程序用于示意小块分配后sbrk(0)读到的 program break 可能上移1MiB级别分配常落在远离堆的地址且brk 不变表明走了mmap。请以本机 glibc 与pmap输出为准。#define_GNU_SOURCE#includestdio.h#includestdlib.h#includeunistd.hintmain(void){printf(Initial brk %p\n,sbrk(0));void*p1malloc(64);printf(After malloc(64): p1%p, brk%p\n,p1,sbrk(0));void*p2malloc(1024u*1024u);printf(After malloc(1MiB): p2%p, brk%p\n,p2,sbrk(0));free(p1);free(p2);return0;}编译运行示例gcc -O0 demo.c -o demo ./demo可用pmap -x pid在另一终端对运行中进程查看[heap]与[ anon ]映射差异。观察点小块大块示意p与brk关系常在堆范围内brk可能增大地址常落在mmap 映射区brk可不变现代分配器的线程缓存tcmalloc / jemalloc共同目标让高频小对象的malloc/free尽量在本线程上下文完成减少全局锁与系统调用仅在缓存空或满时与中央结构或OS交互。多级结构概念操作系统跨线程共享每线程批量搬运申请与归还页线程缓存(tcache / thread cache)中央缓存 / Arena bins页堆 / Chunkbrk、mmap 等tcmalloc 与 jemalloc纲要项目tcmalloc概念jemalloc概念小对象按size class进入线程本地free list不足时向central freelist批量取tcache优先不足时从arena的bin批量填充中央层Central free list协调多线程与页堆多个arena线程绑定/轮询降低锁竞争大块常绕开线程缓存在页级结构用更粗粒度同步huge类对象可走mmap/munmap减少与普通堆混杂演进新版本有per-CPU cache等优化方向以官方文档为准extent / slab等设计控制碎片与元数据开销注意「无锁」多为快速路径上无全局互斥跨线程归还、中央层批量移动仍可能有原子或锁。具体数据结构与阈值以gperftools / jemalloc对应版本说明为准。观测与调优线索手段能回答的问题/proc/pid/maps/pmap -x堆、[anon]、[heap]等映射范围与是否出现大块mmapmallinfo/malloc_statsglibc分配器内部视角部分接口已标记过时仅作粗查LD_PRELOADlibjemalloc.so等对比默认malloc与jemalloc/tcmalloc在锁竞争、碎片上的差异perf/strace -e brk,mmap,munmap热点路径是否频繁syscall注意strace开销极大仅短跑诊断应用层减少分配次数对象池、arena、对齐大结构体、避免线程间频繁交叉free参考链接相关主题公开文章背景阅读https://mp.weixin.qq.com/s/dEIG1RgGVZWscfe6K4MGUAmalloptman malloptLinuxjemalloc 设计文档https://jemalloc.net/tcmallocgperftoolshttps://github.com/gperftools/gperftools内核页大小、mmap 阈值、ASLR与libc 版本均会影响实验输出生产环境性能问题应结合LD_PRELOAD分配器、perf、分配器统计与真实负载分析。