堆内存起始地址为bss段末尾,增长方向自低地址向高地址;sbrk(0)返回当前program break位置,/proc/[pid]/maps中[heap]行的起始地址即堆起始地址,结束地址随sbrk调用动态扩展。

怎么看进程的堆内存起始和增长方向
sbrk 系统调用返回的地址就是当前堆的“边界”,但它不等于堆的起始地址,也不代表已分配的全部堆内存。实际堆内存从 brk 系统调用设定的初始位置开始(通常是 BSS 段末尾),后续每次 sbrk 调用只是移动这个边界。你看到的 sbrk(0) 返回值,是当前 brk 位置,不是“已用堆”的起点。
常见误区是以为两次 sbrk(1024) 后指针会变——其实不会,sbrk 是“设边界”,不是“分配新块并返回新地址”。真正变化的是 /proc/[pid]/maps 中 heap 行的结束地址。你可以用 sbrk(0) 获取当前值,再调一次 sbrk(4096),然后立刻查 /proc/[pid]/maps,就能看到 heap 行末尾地址增加了 4096。
- 堆内存映射在
/proc/[pid]/maps中固定标记为[heap],只有一行(除非使用mmap显式申请) -
sbrk改变的是这一行的结束地址,起始地址通常不变 - 如果程序用
malloc分配大块内存(≥128KB),glibc 会改用mmap,这部分不会出现在[heap]行里,而是在独立的匿名映射行中
/proc/[pid]/maps 里怎么识别栈和堆
栈和堆在 /proc/[pid]/maps 中靠标识符区分,不是靠地址高低或名字猜测。堆一定带 [heap] 标签;栈一定带 [stack] 标签(主线程)或 [stack:tid](线程栈)。别信“高地址是栈、低地址是堆”这种笼统说法——ASLR 开启后地址随机,且线程栈可能分散在各处。
例如某进程的 maps 片段:
Linux AI 服务器 — 将 Linux 服务器转换为本地 AI 推理集群。使用 systemd、NVIDIA CUDA 实现无头 Linux AI,零 GUI 开销。
00400000-00401000 r-xp 00000000 08:01 1234567 /bin/cat ... 203e000-2040000 rw-p 00000000 00:00 0 [heap] ... 7ffd12345000-7ffd12366000 rw-p 00000000 00:00 0 [stack]
注意:[heap] 行权限是 rw-p(可读写、私有、不执行),[stack] 行也是 rw-p,但内核会在栈顶上方预留一个不可访问的 guard page(在 maps 中不单独显示,但 cat /proc/[pid]/maps 时你能看到它紧邻栈区域的空隙)。
- 主线程栈只有一行
[stack];每个 pthread 创建的线程都有自己的[stack:tid]行 - 如果没看到
[heap]行,说明进程至今没调用过sbrk或brk(比如纯用mmap分配内存的程序) -
[heap]行缺失 ≠ 没有堆内存 —— glibc 的 malloc 可能全程用mmap,此时堆内存表现为多个rw-p匿名映射
为什么 pstack 看不到完整栈信息或报 only 32 bit objects supported
pstack 本质是轻量级 gdb 封装,依赖目标进程的 ELF 架构匹配和调试符号。报 only 32 bit objects supported 不是因为进程是 32 位,而是你用的 pstack 二进制本身是 32 位的,却去 attach 了 64 位进程。Ubuntu/Debian 官方源里的 pstack 包早已废弃,编译出的版本往往硬编码了寄存器名(如 EIP/EBP),在 x86_64 上直接失效。
- 替代方案优先用
gdb -p [pid] -ex "thread apply all bt" -ex quit,它不依赖预编译工具链 -
cat /proc/[pid]/stack只对内核线程有效,用户态进程该文件为空 - 若需实时栈深监控,不要依赖
pstack,改用/proc/[pid]/stat中的stksize字段(单位 KB),它反映内核记录的当前栈用量,比解析 maps 更直接
查 RSS、VmRSS、VmStk 时容易混淆的三个值
cat /proc/[pid]/status 里的 VmRSS、VmStk 和 ps 输出的 RSS 数值经常不一致,不是 bug,是统计口径不同:
-
VmRSS:内核维护的“常驻物理内存”估算值,包含共享库、堆、栈、数据段等所有已加载进物理内存的部分,单位 KB -
VmStk:仅主线程栈占用的物理内存大小,不含线程栈,单位 KB;它只是VmRSS的子集 -
ps的RSS:用户态工具计算值,基于/proc/[pid]/stat的第 24 字段(rss),单位是页(PAGE_SIZE,通常是 4KB),所以数值上 ≈VmRSS÷ 4,但因采样时机差异可能差 1~2 页
真正想看栈是否快爆了,别只盯 VmStk,得结合 ulimit -s 查软限制(默认 8192 KB),再用 grep -i stack /proc/[pid]/limits 确认实际生效值。栈溢出前内核不会主动告警,只有踩到 guard page 才触发 SIGSEGV。










