直接回答:进程的虚拟地址空间从低到高大致分为:代码段(text,只读可执行)、已初始化数据段(data)、未初始化数据段(BSS)、堆(heap,向高地址增长)、内存映射区(mmap 区,动态库和文件映射放在这里)、栈(stack,向低地址增长);64 位 Linux 下高半区留给内核,用户态不可访问。

展开解析:几个值得记的细节:malloc 分配小内存(默认小于 128KB,即 M_MMAP_THRESHOLD)用 brk 在堆顶扩展,大内存走 mmap 单独映射一块匿名页;free 之后,brk 给的内存通常留在堆里复用、不归还内核(这就是 RSS 降不下来的常见原因),mmap 的则立即 munmap。栈由内核按需自动增长,默认上限 8MB(ulimit -s),超过触发段错误,也就是栈溢出。各段基址受 ASLR 随机化,缓解缓冲区溢出攻击。实际布局可以直接看:

cat /proc/self/maps | head

每一行标注了地址范围、权限和对应的文件(可执行文件、libc.so、匿名映射等)。

追问方向:为什么堆和栈相向增长?多线程程序的线程栈在哪个区域?如何关闭 ASLR 以及为什么调试 core dump 时有时会关它?