一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

Linux查看进程堆内存消耗的办法 Linux下gdb排查内存泄漏详解

时间:2026-06-04 08:04:06 编辑:袖梨 来源:一聚教程网

最实用方法是cat /proc/[pid]/maps | grep "[heap]"定位堆段起止地址,再用printf计算字节数;VmData字段粗略反映数据段(含堆),但JVM等场景不适用;pmap -x [pid]的ANON列可精准识别堆外匿名内存泄漏。

怎么看进程的堆内存到底占了多少

Linux 进程的“堆内存”不是单一数值,/proc/[pid]/statm/proc/[pid]/status 里没有直接叫“heap”的字段。真正反映堆使用情况的,是匿名映射中由 sbrkbrk 扩展的那一块——也就是 Heap 段,它在 /proc/[pid]/maps 中表现为起始地址接近 [heap] 标记、权限含 rw、且不关联任何文件的一段。

最实用的办法是:

  • cat /proc/[pid]/maps | grep "[heap]" 定位堆段起止地址(如 01234000-01256000 rw-p ... [heap]),再用 printf "%dn" $((0x01256000 - 0x01234000)) 算出字节数(约 139264 字节)
  • 更粗略但快:看 cat /proc/[pid]/status | grep -E "VmData|VmStk|VmExe",其中 VmData 是数据段(含堆),对纯 C 程序较准;JVM 进程则意义不大,因为堆外内存走的是 mmap 匿名映射
  • 注意:VmRSS 是全部驻留内存(含堆、栈、共享库、DirectByteBuffer 等),不能等同于堆大小

gdb 能不能直接看到 malloc 分配点

可以,但必须满足两个前提:程序用 -g 编译、且没开 -O2 以上激进优化(否则变量被内联或寄存器化,堆栈信息失真)。

常用操作链:

  • 启动调试:gdb -p [pid]gdb ./a.out core
  • 监控分配:(gdb) catch syscall brk(gdb) catch syscall mmap,触发后用 info registersrdi/rsi(size 参数)
  • 回溯调用:(gdb) bt 查当前堆栈;若想捕获每次 malloc,需设断点:break __libc_malloc(glibc 实现),再用 commands; bt; continue; end
  • 别依赖 malloc_stats():它只打印 arena 汇总,不带调用上下文;且多线程下输出可能错乱

为什么 pmap 比 top 更适合定位堆外泄漏

因为 topRES 是进程所有物理内存之和,而 pmap -x [pid] 能按页、按映射类型拆解——你一眼就能看出哪类匿名映射在疯长。

重点关注这几列:

  • ANON 列非零且持续增大 → 典型堆外泄漏(如 DirectByteBuffermmap(MAP_ANONYMOUS)、C++ new
  • mapped 列对应文件映射,通常稳定;若它涨了,检查是否反复 mmap 同一文件却没 munmap
  • total 行末尾的 ANON 总和,就是当前进程所有匿名内存占用,和 RES 差值大,说明大量内存被缓存或交换了

执行 pmap -x [pid] | tail -n 1 可快速抓总览;配合 watch -n 5 'pmap -x [pid] | tail -n 1' 动态观察变化。

gdb + pmap 组合排查时最容易忽略的细节

真实泄漏往往藏在“合法但失控”的行为里,比如日志缓冲区不断 realloc、连接池未 close 导致 mmap 积累、甚至 pthread_create 后线程栈未回收。

务必确认以下三点:

  • 目标进程是否启用了 LD_PRELOAD 替换 malloc(如 tcmalloc、jemalloc)?此时 __libc_malloc 断点无效,得查对应库的符号,例如 break tc_malloc
  • pmap 输出中的地址范围是否与 gdbinfo proc mappings 一致?不一致说明进程内存已重映射,需重新 attach
  • gdb 查到可疑指针后,别只看 print *ptr;要结合 info proc mappings 确认该地址落在哪个映射段,再反查 /proc/[pid]/maps 判断它是堆、栈还是 mmap 区

堆外泄漏没有 GC 帮你兜底,每一块漏掉的 mmap 都会实实在在吃掉物理内存。工具只是眼睛,关键还是理解进程地址空间里谁有权申请、谁负责释放。

热门栏目