A10 — Memory Analysis Tools: malloc_stats, Valgrind Massif, pmap

Key Insight: The first step to optimizing memory is knowing where it is being used.


malloc_stats: glibc Allocation Statistics

Bash
malloc_stats() 输出所有 arena 的统计信息:

#include <malloc.h>
malloc_stats();

输出示例:
  Arena 0:
    system bytes    =  1048576
    memory bytes   =  1048576
    96 malloc-chunks in use (12345 bytes)
  Arena 1:
    system bytes    =  2097152
    ...

字段解释:
  system bytes:从系统获取的总内存
  memory bytes:实际使用的内存
  96 malloc-chunks:正在使用的块数
  12345 bytes:正在使用的字节数

实时查看:
  #include <mcheck.h>
  mtrace();  // 开启 malloc 跟踪
  // 运行程序后
  // MALLOC_TRACE=/tmp/trace ./program
  // mtrace ./program /tmp/trace

Bash
mallinfo:结构化的分配信息:

#include <malloc.h>
struct mallinfo mi = mallinfo();

mi.arena:所有 arena 的总大小
mi.ordblks:空闲块数
mi.hblks:mmap 分配的大块数
mi.hblkhd:mmap 分配的总字节数
mi.uordblks:正在使用的字节数
mi.fordblks:空闲字节数
mi.keepcost:top chunk 的大小(可释放)

使用示例:
  struct mallinfo mi = mallinfo();
  printf("使用中: %d bytesn", mi.uordblks);
  printf("空闲: %d bytesn", mi.fordblks);
  printf("mmap 大块: %d bytesn", mi.hblkhd);

Valgrind Massif: Heap Profiler

Bash
valgrind --tool=massif ./program

生成 snapshot 文件:massif.out.12345

分析工具 ms_print:

$ ms_print massif.out.12345

  GB
  1.972^                                                  #
      |                                               ####
      |                                            ###    #
      |                                         ###      ##
      |                                      ###        ##
      |                                  ###            #
      |                               ###               #
      |                            ###                  #
      |                        ###                      #
      |                   ###                          #
      |               ###                              #
  0 +-------------------------------------------#------->GB
    0       50      100      150      200      250      300

  Snapshot 100 (MB)
  堆峰值:1.972 GB(发生在 250s)
  堆详细分配(Snapshot 100):
    0x40000000: 0x100000 (1024KB) 0x... (malloc)
    ...

ms_print 输出:
  - 每列 = 一个 snapshot(时间序列)
  - 每个 snapshot 显示堆使用量
  - 峰值在哪里(Peak)
  - 详细堆栈(包含分配位置)

Bash
ms_print 的内容解读:

Snapshot 50 details:
  n0     "main" malloc @ 0x400567
    0x400567: allocate 1024 bytes (test.c:10)
    0x400789: func() (test.c:25)
    0x400ABC: main (test.c:50)

字段:
  n0:节点编号(堆快照中的编号)
  "main":malloc 调用的函数栈
  test.c:10:具体代码位置
  1024 bytes:分配的大小

峰值信息:
  Peak GB: 1.972 (在 Snapshot 200)
  峰值占比:99% 来自 main() 中的某个循环

pmap: Viewing Process Memory Mappings

Bash
pmap 查看进程的完整内存布局:

$ pmap -x 12345

12345:   ./program
Address           RSS   Dirty   Mode   Mapping
00007f...          4 KB     4 KB  r-xp  [vsyscall]
00007f...      16384 KB  16384 KB  rw-p  [anon]
00007f...          4 KB     4 KB  rw-p  [stack]
00007f...       8192 KB  8192 KB  rw-p  [heap]
...

字段:
  Address:虚拟地址范围
  RSS:Resident Set Size(实际驻留内存)
  Dirty:脏页数量
  Mode:权限(r-xp=代码,rw-p=数据)
  Mapping:映射类型(文件路径/[heap]/[stack]/[anon])

pmap -X(详细):
  - 显示每个页的详细信息
  - 更详细的内存统计

Bash
pmap 查看泄漏:

$ pmap -x 12345 | sort -k3 -n -r | head -20

  // 按 RSS 排序,找出内存占用最多的区域

  00007f... 16384 KB ... [anon]   ← 可能是泄漏(mmap 大块)
  00007f...  8192 KB ... [heap]   ← 堆(brk 管理)

/proc/self/status and /proc/self/statm

Bash
/proc/self/status:

$ cat /proc/self/status

VmPeak:    123456 kB   // 峰值虚拟内存
VmSize:    12345 kB    // 当前虚拟内存
VmRSS:      8192 kB    // 实际驻留内存
VmData:     4096 kB    // 数据段大小(堆)
VmStk:       128 kB    // 栈大小
VmSwap:        0 kB    // 交换出去的大小

VmPeak 持续增长 = 内存泄漏的信号

---

/proc/self/statm:

$ cat /proc/self/statm

8192 4096 12345 0 0 1000 0
│    │    │    │ │   │   └── 0(未知)
│    │    │    │ │   └────── RSS pages
│    │    │    │ └────────── size pages(总虚拟)
│    │    │    └──────────── shared pages
│    │    └────────────────── text pages(代码)
│    └─────────────────────── resident pages
└──────────────────────────── total pages

字段:
  total: 总虚拟页数
  resident: 驻留页数
  shared: 共享页数
  text: 代码页数
  data: 数据页数(堆+栈+数据)

perf and Flame Graphs for Memory Analysis

Bash
perf record 跟踪内存访问:

$ perf record -g -e cycles:u ./program
$ perf report

输出:
  - cycles:u:用户态 CPU 周期
  - -g:记录调用栈
  - 生成 report,分析热点

火焰图(FlameGraph):

$ perf record -g -e cycles:u ./program
$ perf script | ./stackcollapse-perf.pl | ./flamegraph.pl > flame.svg

火焰图:
  - Y 轴 = 调用栈深度
  - 方块宽度 = 执行时间
  - 从下往上看,可以看到内存分配的热力图

分析内存热点:
  - 哪些函数分配最多内存
  - 哪些路径触发最多分配
  - 优化热点,减少不必要的分配

Summary

  • malloc_stats(): Real-time output of all arena statistics (system/used/free bytes)
  • mallinfo: Structured statistics (arena/ordblks/hblks/hblkhd/uordblks/fordblks)
  • valgrind –tool=massif: Heap analysis; ms_print to view peaks and timeline; locate leaks
  • pmap -x: View process virtual address mappings (RSS/permissions/type); sort by RSS to find top consumers
  • /proc/self/status: VmPeak/VmRSS/VmData/VmSwap; sustained growth indicates a leak
  • perf + Flame Graphs: Tracks CPU cycles; generates flame graphs; analyzes allocation hotspots

Next (A11): How do user space and kernel space memory interact? mmap, brk, and zero-copy principles.


Last modified: 2024年10月1日

Author

Comments

Write a Reply or Comment

Your email address will not be published.