pmap -x 显示 [anon] 占用巨大但 smaps 里 Private_Dirty 很少

8次阅读

[anon]的Size大而Private_Dirty小是正常现象,因Size仅表示虚拟地址空间大小,而Private_Dirty才反映实际占用的物理内存;这是延迟分配机制所致,如malloc/mmap仅预留虚拟地址,写入时才分配物理页。

pmap -x 显示 [anon] 占用巨大但 smaps 里 Private_Dirty 很少

这是 linux 内存管理中一个常见但容易误解的现象:pmap -x 显示 [anon] 区域的 Size(虚拟地址空间大小)非常大,而对应进程的 /proc/PID/smaps 中该区域的 Private_Dirty 却很小(甚至为 0)。这并不表示内存泄漏或异常,而是反映了虚拟内存与物理内存的本质区别

为什么 [anon] 的 Size 巨大但 Private_Dirty 很小?

[anon] 是内核对匿名映射(如 mallocmmap(MAP_ANONYMOUS))分配的虚拟内存区域的统称。它的 Size 字段(pmap -x 第二列)代表的是该段虚拟地址空间的长度 —— 它只是“划了块地”,不等于实际用了多少物理内存。

真正反映物理内存占用的是 smaps 中的:

  • Private_Dirty:进程独占、已修改、尚未写回磁盘(即必须保留在 RAM 中)的私有页;
  • RSS(在 smaps 中为 Rss):当前驻留在物理内存中的总页数(含共享页、干净页、脏页);
  • MMU Page Tables 开销:大块虚拟地址空间本身会消耗少量内核内存(页表项),但这和用户态内存使用无关。

所以,Size 大 + Private_Dirty 小 = 进程申请了大量虚拟地址空间,但尚未真正写入(触发缺页中断),或只写了其中极小一部分。

典型场景:延迟分配(Lazy Allocation)和内存预分配

Linux 默认启用 overcommitlazy allocation:调用 mallocmmap 时,内核只建立页表项、不分配物理页;直到第一次写入某页,才触发缺页中断,真正分配一个物理页并清零(即所谓的 “zero page” 优化)。

常见例子包括:

  • jvm 初始只 reserve 虚拟空间(如 -Xmx4g),实际使用随对象分配增长;
  • glibc malloc(ptmalloc)为避免频繁系统调用,会预先 mmap 大块 [anon] 区域作为 arena,但大部分长期空闲;
  • 某些数据库中间件显式 mmap(MAP_ANONYMOUS|MAP_NORESERVE) 预留地址空间,按需 touch。

如何确认是否真有内存压力?

别只看 pmap -x 的 Size,应结合以下指标交叉判断:

  • /proc/PID/status 中的 VmRSS(≈ RSS 总和)和 VmData(数据段+堆的 RSS);
  • cat /proc/PID/smaps | awk '/^Size:/ {s+=$2} /^Rss:/ {r+=$2} /^Private_Dirty:/ {d+=$2} END {print "Size:",s,"KB; Rss:",r,"KB; Dirty:",d,"KB"}' 汇总关键值;
  • 观察 free -hcat /proc/meminfoMemAvailable 是否持续偏低;
  • perf record -e 'mem-loads,mem-stores' -p PIDsmaps_rollup 工具分析实际访问模式。

需要关注什么?什么时候算异常?

只要 Private_DirtyRSS 稳定、未持续增长,且系统无 OOM 或显著 swap,Size 大就是正常行为。

真正需警惕的情况是:

  • Private_Dirty 持续线性增长,且不释放(如内存泄漏);
  • smaps 中出现大量 MMUPageSize: 2MBTHP 相关字段,但应用未主动启用透明大页,可能暗示 THP 合并异常导致碎片或浪费;
  • pmap -x 显示数百个零碎 [anon] 区域(每块几 MB),可能是 malloc 频繁分配/释放导致的虚拟地址碎片,影响 TLB 效率(可用 cat /proc/PID/maps | grep anon | wc -l 快速统计)。
text=ZqhQzanResources