news 2026/10/1 1:13:55

Linux内存排查利器:/proc/pid/smaps核心字段解析与实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux内存排查利器:/proc/pid/smaps核心字段解析与实战

有一类内存问题,会把一个Linux老兵逼到挠头:free 报告可用内存只剩几百MB,top 按 RES 排序杀出一个进程,数字大得吓人。你点开 pmap -x,看到的却是一长串十六进制地址,根本读不出信息。我早期排查这类问题也是这样——对着 /proc/[pid]/maps 干瞪眼:每个地址区间的权限和文件路径我都认识,但它占了多少物理内存、多少是自己私有的、多少可以回收,一个都答不上来。直到我真正啃下 /proc/[pid]/smaps,才算是第一次摸到了进程内存的纹理:哪一段虚拟内存区域占了几个G,私有的还是共享的,干净页还是脏页,甚至有多少换到了swap里,全都写得明明白白。如果你是开发、运维或者SRE,只要你的服务有一天会吃内存,smaps 就该是你的必读文件。这篇就把它从里到外拆一遍,顺便附上我用它定位内存异常的真实排查记录。

1. 一张smaps长什么样:先看VMA骨架,再谈统计字段

1.1 VMA:smaps文件里每一节的描述单位

要读懂smaps,先得明白一个概念:VMA(Virtual Memory Area,虚拟内存区域)。进程的虚拟地址空间不是一整块,而是被内核按"地址连续、权限一致、来源相同"的原则切成很多段。每一段就是一个VMA,比如代码段是一个VMA、堆是一个VMA、栈是一个VMA、每个mmap的共享库又是一个VMA。

/proc/[pid]/maps 就是把这些VMA逐个列出来,格式大概是这样:

00400000-00406000 r-xp 00000000 fd:00 2638625 /usr/bin/cat

六列依次是:虚拟地址区间、权限(r/w/x/p/s,p表示私有映射,s表示共享映射)、文件偏移、设备号(主:次)、inode号、映射文件路径。但maps只告诉你"这块区域能读能写",不告诉你它实际消耗了多少物理内存。

smaps把maps的每一行当成一个小节的标题,标题下面跟着十多个统计字段,回答"这个VMA到底吃了多少内存"。想知道一段区域是干什么的,看第一行;想知道它真的占了多少,看下面的字段。这就是smaps设计的核心逻辑:按VMA粒度记账。

值得多说一句的是,第一行里的路径未必都是文件。常见的几种VMA标注包含:[heap]代表堆区,[stack]代表栈区,[anon]代表匿名映射(通常是mmap分配的内存,也可能是线程栈),[vdso]、[vvar]、[vvar_vclock]这些是内核映射给用户态的小块辅助代码,还有带完整路径的是文件映射。路径后面出现(deleted),意味着映射的文件已经被删除但映射还在——这是一个非常重要的排查线索,后面实战部分我会专门讲。

1.2 一个真实条目的逐段解读

随便找一个进程,比如pidof cat拿到PID之后执行cat /proc/PID/smaps,你看到的每个VMA小节长这样:

00400000-00406000 r-xp 00000000 fd:00 2638625 /usr/bin/cat Size: 4 kB KernelPageSize: 4 kB MMUPageSize: 4 kB Rss: 4 kB Pss: 4 kB Shared_Clean: 0 kB Shared_Dirty: 0 kB Private_Clean: 4 kB Private_Dirty: 0 kB Referenced: 4 kB Anonymous: 0 kB LazyFree: 0 kB AnonHugePages: 0 kB ShmemPmdMapped: 0 kB FilePmdMapped: 0 kB Shared_Hugetlb: 0 kB Private_Hugetlb: 0 kB Swap: 0 kB SwapPss: 0 kB Locked: 0 kB THPeligible: 0 ProtectionKey: 0

不同内核版本字段会略有增减,但主干就这些。初次接触的人很容易被二十多个数字吓退,我建议把它们分成三类记忆:

第一类是VMA自身属性的统计:Size是这个VMA的虚拟内存大小,KernelPageSize和MMUPageSize说明页大小情况。第二类是物理驻留与共享分摊的统计:Rss、Pss、Shared_Clean、Shared_Dirty、Private_Clean、Private_Dirty,这一组是最常用的核心指标。第三类是特殊页与杂项:Swap、SwapPss、Locked、AnonHugePages、THPeligible、ProtectionKey这些,对应透明大页、mlock锁页、内存保护键等高级特性。

养成先扫标题行、再看核心统计、最后看特殊字段的习惯,smaps就一点也不吓人了。接下来的章节,我会把这三组字段逐一拆开讲,重点说清楚它们背后的计算逻辑和业务含义。

2. 逐项解剖统计字段:驻留、共享、脏页与Swap的算法逻辑

2.1 Rss和Pss:同一个VMA,两种记账口径

先说最常用的两个:Rss(Resident Set Size,常驻集大小)和Pss(Proportional Set Size,按比例分摊的常驻集大小)。

Rss表示这个VMA里映射了多少页在物理内存中,不管这些页是不是也让其他进程共享了。假设一个进程加载了libc.so,其中某个物理页同时被三个进程映射,那么每个进程的smaps里都会把这一页算进自己的Rss。从单个进程视角看没问题,但如果把系统所有进程的Rss加在一起,物理内存会被重复计算好几遍——这就是你常常看到"所有进程RES加起来远大于free里的used"的原因。

Pss修正的就是这个问题。它的计算思路是:对于每个物理页,用它的大小除以映射到该页的进程数,然后把每个VMA里的分摊值加总。还是那个被三个进程共享的libc页面,内核就会给每个进程只记下三分之一。Pss把"进程视角的账"转换成了"系统资源视角的账",在评估共享内存对系统整体内存压力影响时,比Rss可靠得多。

我处理过不少内存告警,一个常用的经验是:看进程自身的内存增长趋势用Rss,衡量它对整机内存的真实消耗用Pss。两者差值越大,说明这个进程的共享映射越多。

2.2 Shared_Clean与Private_Dirty:四个值的分组账本

紧接在Pss后面的四个字段,把Rss又精细切分了一遍:

  • Shared_Clean: 共享映射页,且页面是"干净"的。所谓干净,指页面内容与磁盘上的文件副本一致,系统需要内存时可以直接丢弃,无需回写。
  • Shared_Dirty: 共享映射页,但页面被修改过,属于"脏页"状态,回收前必须先写回磁盘。
  • Private_Clean: 私有映射页,干净状态。进程可能是通过MAP_PRIVATE映射了一个文件但还没写。
  • Private_Dirty: 私有映射页,脏状态,必须回写或换出后才能回收。

这四个值加起来正好等于Rss。我记这组字段时有个口诀:Shared对应多进程共享,Private对应本进程独占,Clean是随时能丢的干净页,Dirty是必须先处理的脏页。排查内存压力时,Private_Dirty尤其值得关注,因为它往往代表进程真正私有的、动态产生的数据——堆上分配的内存、写时复制后的匿名页,大多落在这里。

一个常见的坑是:看到进程Rss很高,以为是内存泄漏,结果看Shared_Clean占了绝大部分,说明它加载了大量共享库或文件映射的只读页。这些页实际上对整机内存压力很小,被回收的风险也低,因为随时可以从文件重新读入。如果看到Private_Dirty持续上涨而业务又没增长,这才更可能指向泄漏或者缓存失控。

2.3 Swap、Anonymous、Referenced、Locked与LazyFree:杂项字段各管一摊

Swap记录的是:这个VMA里有多少页已经被换出到swap交换空间。很多人容易忽略一个细节——页面换出去了,但它仍然属于这个VMA,地址空间也在,只是不在物理内存里。所以排查内存占用时,Rss + Swap才是这类页面占用的总成本。

Anonymous表示匿名页的驻留大小。匿名页指与文件没有任何关联的页面,典型来源是堆、栈、写时复制产生的私有页、以及mmap的MAP_ANONYMOUS映射。这个字段对判断"进程是不是在搞匿名内存大头"很有用。配合Swap看,如果Anonymous很高而Swap也很高,说明进程占内存后被换出了一部分,这种场景下Pss + SwapPss能更准确地估计它对系统造成的实际成本。

Referenced统计的是最近被访问过的页面数量。内核会定期扫描页表更新引用位,所以这个值间接反映了VMA里的页面在近期是否真的被读写了。这个字段不算精确,但在区分"堆里分配了但没用"和"堆里分配了并且在用"这两类情况时,它是很好的辅助判断依据。

Locked对应被mlock或mlockall锁定的页面,这些页不允许被换出,通常用于需要保护敏感密钥或实时性要求高的场景。LazyFree则是内核4.12之后新增的,记录通过madvise(MADV_FREE)标记为可释放的页面。这类页面在被内核回收之前,进程仍可以读,但一旦内核缺页优先把它们干掉。

我建议把下面这张表收藏起来,排查现场对照着看最方便:

字段含义排查时的解读
RssVMA驻留物理内存总量进程视角的内存占用
Pss按共享进程数分摊的驻留量系统视角的真实成本
Shared_Clean共享且干净的页多为共享库、文件只读映射,可随时回收
Shared_Dirty共享且修改过的页需要先回写才能回收
Private_Clean私有且干净的页多为映射文件尚未修改
Private_Dirty私有且修改过的页堆、匿名数据主要落在这里,重点盯防
Anonymous匿名页驻留量判断是否堆/匿名内存涨了
Swap已换出到交换空间的页常和Rss一起看进程总内存成本
SwapPss按比例分摊的Swap量衡量swap对整机的分摊影响
Referenced最近被访问的页判断VMA里是否真的有热数据
Lockedmlock锁定的页锁定意味着不可换出
LazyFreeMADV_FREE标记的页可回收但仍在账上的页

这组字段吃透了,smaps里百分之八十的信息就已经到你手里了。

3. 哪个数字才是真实占用的答案:RSS、PSS与共享页分摊的计算

3.1 三个进程加载同一份libc的真实账本

为了把RSS和PSS的区别说透,我算一笔具体的账。

假设系统同时运行三个进程A、B、C,它们都用mmap方式映射了同一个4MB大小的libc.so文件。内核只在实际访问页面时才会把文件页搬进物理内存,这里假设三个进程把所有页面都触达了,那么libc.so在物理内存里一共只有4MB(系统里只存在一份物理页)。

但从每个进程的smaps看,Rss都把整份libc的4MB记在了自己名下。于是:

  • 每个进程的Rss:约4MB
  • 三个进程Rss之和:约12MB
  • 系统真实物理内存占用:约4MB
  • 每个进程的Pss:约4 / 3 = 1.33MB
  • 三个进程Pss之和:约4MB

这个例子就是RSS“高估”的原因。你把top里所有进程的RES加起来,经常比free里的used高出不少,就是因为共享页被重复计数了。换成PSS求和,结果会贴近真实物理内存用量。

在生产环境里,Java、Node.js这类运行时依赖一堆共享库的程序特别明显。我曾经观察过一台跑着12个微服务容器的机器,所有容器RSS求和超出实际内存用量近40%,但换成PSS求和之后基本吻合。从那以后,我在给内存做“按进程归因”时,标准动作都是优先看PSS。

3.2 PSS的两个统计陷阱:精确但不是绝对精确

PSS不是银弹,它有两个容易被忽略的误差来源。

第一个是页面可能被同一个进程映射到不同的VMA里,而不是被不同进程共享。比如进程对同一个文件做了多次mmap映射,同一个物理页出现在多个VMA的PSS计算中,最后在进程内部就被重复计数了。内核在计算PSS时,是以VMA为单位逐个统计的,它并不去全局查重。因此内核文档里也明确警告过:系统所有进程的PSS之和,和实际物理页用量之间仍可能有一定偏差。

第二个误差来自共享进程数变化的瞬时性。PSS的“按进程数均分”是一个近似模型,真实物理页被映射到的进程数可能在一两个毫秒内就变了。你抓取到的smaps快照,只能反映那一瞬间的分摊情况。所以PSS适合做趋势观测和粗粒度归因,不适合当成绝对精确的账本。

还有一个常见误区是:拿free里的used值和全部进程PSS之和做等式对比。这肯定对不上,因为内核自身的页面、驱动使用的页面、page cache里还没被任何进程映射的页面都不计入任何进程的PSS。PSS之和只能解释“哪些进程消耗了大部分内存”,不能解释“整机的内存到底去哪了”。

3.3 业务场景里到底该看谁

结合我自己的排查经验,这三类指标各有主战场:

判断某个进程是不是内存大户,看Rss——它对应top里RES那一列,大家习惯上都用这个值横向比较。评估某个进程对整机内存的实际压力,看Pss——共享库、共享内存多的程序,Pss往往只有Rss的一半甚至更少。排查内存泄漏或异常增长,则要盯着Private_Dirty + Swap的组合——它们最接近“进程自己搞出来的、别人帮不了忙”的内存成本。

顺序也很重要。线上告警一来,我通常先按Rss排序找到嫌疑进程,再切到smaps看Pss和Private_Dirty,判断它到底是真大户还是“表面大户”。这个习惯能避免很多误杀,特别是在容器化场景里——两个容器共享同一个只读镜像层时,Rss虚高非常常见,但Pss会告诉你真实压力远没有那么夸张。

4. 实战排查:一次真实的内存虚高定位全记录

4.1 现场现象:RES爬到10GB以上,业务流量却纹丝不动

某次线上故障,监控显示一台8C16G的机器的可用内存掉到了1GB以下,连续触发warn级别告警。登录机器后我按惯例先跑了一遍top,看到某个内部中间件进程的RES已经爬到11.8GB,而这个进程平时的正常水位在5GB左右。更奇怪的是,业务调用量并没有明显增长。以“进程突然多占了6GB内存”为起点,我开始逐层往下拆。

第一步是用/proc/[pid]/status快速确认总账:

grep -E "VmRSS|VmData|VmExe|VmStack|VmLck" /proc/<pid>/status

结果里VmRSS是11.8GB,VmData高达9.4GB。VmData代表数据段大小,基本上是堆和私有匿名映射的天下;VmExe、VmStack都很正常。这说明大头确实在堆和匿名映射方向,而不是共享库膨胀。这为后续缩小了排查范围。

4.2 给每个VMA按Pss排序,顺手写个小脚本

知道是堆的问题还不够,我还想看到底是哪个VMA在吞内存。这时候smaps的“按VMA记账”优势就体现出来了,但人工翻几千行VMA不现实。我写了个极简的awk脚本,把每个VMA的Pss单独拎出来排序:

awk ' /^[0-9a-f]/ { if (vma) printf "%8d %8d %s\n", pss, rss, path; vma = $0; pss = 0; rss = 0; path = ($NF ~ /^\[/) ? $NF : $NF; } /^Rss:/ { rss += $2 } /^Pss:/ { pss += $2 } END { if (vma) printf "%8d %8d %s\n", pss, rss, path; } ' /proc/<pid>/smaps | sort -k1 -rn | head -20

最终输出里,排在最前面的两个VMA把我彻底看愣了:一个是权限rw-p的地址段,标注为[heap],Pss约6.3GB;另一个是路径带(deleted)标志的文件映射,Pss约2.9GB。[heap]占大头符合预判,但那个deleted文件映射出现在高位,说明事情没那么简单。

这里插一句,awk脚本里的path提取是把整行最后一个字段拿来用,如果路径里带空格会不准确。真实场景里日志文件路径带空格的情况不少见,真要严谨,建议用第一行最后一个字段即使路径包含空格也通常是完整真实的路径(procfs里每行按空格分隔列,路径是最后一列但不会包含空格?实际上procfs路径会保留空格),稳妥起见,直接用$NF然后去匹配已知的lib目录或文件名,或者交给Python脚本解析更安全。

4.3 通往真相的钥匙:deleted文件映射

我先聚焦那个2.9GB的deleted文件映射。smaps这一小节的标题行大概是这样:

7f4d2c000000-7f4d44000000 rw-s 00000000 00:1e 3416108 /tmp/event_stream.dat (deleted)

权限位rw-s里的s表示共享映射,/tmp/event_stream.dat (deleted)表示文件已经被unlink,但这个进程还握着映射不松手。再看下面的统计:Rss2.9GB,Private_Dirty接近0,Shared_Dirty约2.9GB。这说明进程通过共享映射持续写这个已经删除的临时文件,由于文件已经没有目录项,写脏的页面无法通过“回写文件”正常回收——你指望内核把数据写到磁盘?文件都不存在了,写回只会报错。结果就是页面只能一直挂在内存里,越写越多,直到进程退出才彻底释放。

顺着路径,我反查进程里的日志或临时代码逻辑,发现这个中间件有个bug:它会临时创建一个mmap文件用于跨模块传数据,正常情况下用完就unmap,但这个版本里异常分支忘记调用munmap,而文件在创建后的清理任务里被提前unlink,于是留下一个“已删除但仍被持续写入”的映射死结。修复方案是把卸载和清理的顺序做对——先停止写入,再unmap,最后删除文件,并加了一层引用计数防止清理线程抢跑。

4.4 修复之后:结论用一个动作验证

修复代码上线后,我在另一个窗口持续观察进程内存:

watch -n 1 "grep -E 'VmRSS|VmData' /proc/<pid>/status"

进程重启后重新跑业务,VmRSS在半小时内稳定回落到4.8GB左右,之前的11.8GB水位彻底消失。再看smaps时,deleted文件映射的小节已经不在,VMA数量也少了十几个。整机可用内存从不足1GB恢复到了9GB以上。

这次排查最核心的一步,其实就是smaps里那个(deleted)标记。如果没有smaps,只看top和pmap,我可能还要花大半天怀疑堆泄漏;有了smaps,直接定位到一个具体文件映射,范围瞬间缩到一行代码。这也是我坚持给每个线上故障都保留一帧smaps快照的原因——有时候一次内存告警的证据,就藏在这种细节里。

5. 高级字段不鸡肋:THP、巨页、ProtectionKey与VmFlags

5.1 用AnonHugePages判断透明大页是否在吃内存

smaps里有一组字段和透明大页(THP)相关:AnonHugePages、ShmemPmdMapped、FilePmdMapped、THPeligible。很多人从来不细看,但排查某些诡异问题时这组字段是救命稻草。

AnonHugePages表示该VMA里有多少匿名内存是以透明大页(通常是2MB)形式驻留的。THP是内核为减少页表项数量、提升TLB命中率引入的优化,它会尝试把相邻的匿名页自动合并成2MB的大页。听起来很美,但在某些内存分配/释放非常频繁的应用里,THP反而会加剧内存占用——因为合并之后,即使只有一两个字节被修改,这一整块2MB大页在内存压力下也可能无法被拆分为细粒度页来回收。

有一次排查一个Redis-like服务的RSS异常飙升,最后定位到AnonHugePages字段高得反常,业务QPS不高但页碎片化严重。我们在服务启动时显式禁用了THP(通过madvise(MADV_NOHUGEPAGE)或容器启动参数),RSS一下子回落了百分之三十。判断一个VMA是否允许再被THP合并,看THPeligible就够:值为1表示可以,为0表示被排除。

5.2 KernelPageSize与MMUPageSize:巨页场景下的读数差异

KernelPageSize和MMUPageSize平时都是4kB,平平无奇;一旦进程用了hugetlb巨页,这两个数就会变得很有信息量。

KernelPageSize是内核实际使用的页大小,MMUPageSize是硬件MMU管理内存时使用的页大小。在普通4KB页面系统里两者都是4096;在用hugetlb的进程里,KernelPageSize会显示为2048kB(2MB)甚至1048576kB(1GB)。通过smaps里Shared_Hugetlb和Private_Hugetlb这两个字段,你能清楚看到hugetlb页在这个VMA里摊了多少。

这类大页一旦由进程通过hugetlbfs映射,就不会像普通匿名页那样被回收或换出,它们对整机内存容量是明确的、刚性的占用。排查“内存free看着还有,但某个进程无法获得巨页”的问题时,逐进程看Shared_Hugetlb与Private_Hugetlb之和,能准确找出谁在霸占巨页资源。

5.3 ProtectionKey和VmFlags:被大多数人忽略的末行信息

ProtectionKey列出的是一串十六进制形式的保护键值。这是x86的MPK(Memory Protection Keys)特性,允许用户态程序对不同内存区域做细粒度权限隔离。绝大多数业务进程这里都是0,但如果你的服务使用了pkey_alloc、pkey_mprotect这类syscall做安全加固,排查权限异常时就需要对照这个字段确认键值和VMA的对应关系。

smaps某些版本还会额外输出一列VmFlags,例如rd wr mr mw me dw sd。它其实是对最前面权限位的补充说明:rd和wr表示可读可写,mr、mw表示mmap支持读、写,me表示可执行映射,dw表示该VMA是可疑的被写脏映射?不对,dw表示写了但还没同步到文件,sd则是软dirty标记,常用于内存快照与迁移场景。这行字段平常用处不大,但你想确认某个VMA是否有写时复制、是否允许按需换出时,能提供比权限位更细的描述。

这些高级字段平时安静地躺在smaps里,但真遇到“内存看起来够却报错”“进程内存明明不高却触发OOM”这类疑难杂症,它们往往能提供常规视野之外的第二个突破口。

6. 线上监控与脚本化采集:为什么不要频繁cat整个smaps

6.1 smaps的读取成本:不是免费的午餐

smaps好归好,但不能像cat /proc/loadavg那样随便高频调用。每次读取smaps,内核都要遍历目标进程的所有VMA,为每一个VMA汇总页面统计信息,还要持有进程地址空间的相关锁。如果一个进程有几万个VMA,一次smaps读取就会让内核花费不少时间,你监控脚本跑一次也许没事,但如果每5秒抓一遍全机器几百个进程的smaps,CPU开销立刻就能看到。

我见过一个监控脚本事故:运维为了做“进程内存趋势大屏”,每分钟对所有容器触发一次awk解析smaps,业务高峰期CPU直接被监控进程吃掉了不少核心。结论是:smaps适合按需排查,不适合作为常规高频监控项。

6.2 高版本内核的解法:smaps_rollup

Linux 4.14之后内核引入了一个更轻量的节点:/proc/[pid]/smaps_rollup。它把整个进程所有VMA的smaps统计直接汇总成一份输出,字段和smaps完全一致,但不再逐段罗列。对监控来说,体量小好几个数量级,读取开销也下降不少。

我后来把线上内存采集脚本全面切到了smaps_rollup,再用smaps做精细定位。基本的节奏是:每30秒采集一次smaps_rollup里的Rss、Pss、Private_Dirty、SwapPss做趋势;当趋势触发阈值时,再即时抓一帧完整smaps去解析具体VMA。这样既有宏观趋势,又能保留现场细节,采集成本还低了一大截。

不过要注意,smaps_rollup把PSS汇总后会有微小的精度损失,因为每个VMA的共享页分摊比例不同,直接加总后可能和逐段计算的PSS之和存在几KB的偏差。对告警判断毫无影响,但如果你需要做精确到KB的内存证明,还是得自己逐VMA解析smaps。

6.3 一份按Pss排序的进程采集脚本

下面这个脚本,可以在不影响线上性能的前提下,把全机进程按Pss排个序,是定位“谁在买内存”时的好帮手:

#!/bin/bash # usage: pss_rank.sh for pid in $(ls /proc | grep -E '^[0-9]+$'); do if [ ! -r "/proc/$pid/smaps_rollup" ]; then continue fi pss=$(awk '/^Pss:/{print $2}' /proc/$pid/smaps_rollup) rss=$(awk '/^Rss:/{print $2}' /proc/$pid/smaps_rollup) if [ -n "$pss" ] && [ "$pss" -gt 0 ] 2>/dev/null; then cmd=$(tr '\0' ' ' < /proc/$pid/cmdline 2>/dev/null | cut -c1-80) [ -z "$cmd" ] && cmd="[kernel]/$pid" printf "%8d %8d %s\n" "$pss" "$rss" "$cmd" fi done | sort -k1 -rn | head -30

这个脚本里我特意用了smaps_rollup而不是smaps,就是为了降低采集开销。需要注意两点:一是要处理权限问题,很多僵尸进程或属于其他用户的进程读不了smaps,脚本里用可读判断跳过了;二是cmdline为空时通常表示内核线程或正在退出的进程,显示[kernel]/$pid避免漏统计。

如果需要在容器环境里跑,建议把它编译成辅助sidecar,并且用cgroup的memory.stat做交叉验证——容器视角的匿名内存和page cache统计,和smaps的进程视角互补,能发现更多问题。

6.4 smaps不是唯一工具:和status、cgroup、numa_maps的正确配合

最后的建议是:别让smaps单打独斗,它和另外几个内核接口是分工协作的关系。

/proc/[pid]/status里的VmRSS、VmData、VmSwap是从smaps汇总出来的快速视图,适合先看总账。/proc/meminfo和/proc/pagetypeinfo反映整机内存水位与碎片情况,适合判断“是单进程吃内存,还是全机性内存衰退”。/proc/[pid]/numa_maps则提供NUMA视角,能看到哪些VMA里的页散落在哪个NUMA node上,对多路服务器排查跨node访问延迟问题很有价值。cgroup的memory.stat补齐了容器视角:它统计cgroup内所有进程的整体内存,还能区分匿名页、page cache、内核栈等,且支持memory.current做准实时水位判断。

我的习惯是三层联动:smaps_rollup做进程趋势,smaps做单点定位,cgroup memory.stat + meminfo做整机与容器校验。三层交叉验证之后得出的结论,基本上都能站得住脚。

最后分享一个亲测好用的小习惯:排查内存问题之前,先顺手记下当前时间点和业务流量。很多进程的内存本来就在动态波动,光看一帧smaps很容易误判方向;把smaps和其他内存指标组合使用,才是效率最高的方式。对我个人来说,smaps是Linux内存排查中最有“颗粒感”的工具——它不会直接告诉你哪里有Bug,但能让你把模糊的“内存不够”变成具体的“哪一段映射有问题”,而这一步往往就是解决问题的全部关键。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 1:12:44

业余无人机图像数据集实战指南:噪声即特征,落地即检验

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/1 1:11:55

软件测试全流程解析:从测试用例到自动化落地

1. 为什么软件测试是关键环节从入行到现在&#xff0c;我见过太多把软件测试当成“点点点”的团队&#xff0c;也见过因为测试缺位而事故频发的项目。甚至很多刚转行的新人会问&#xff1a;“测试不就是帮开发找茬吗&#xff1f;有什么技术含量&#xff1f;”每次听到这种话&am…

作者头像 李华
网站建设 2026/10/1 1:11:26

ipmitool监控服务器电源与风扇:从命令入门到故障排查

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/1 1:11:25

冬虫夏草YOLO田间检测数据集与实战调优指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/1 1:11:24

C/C++ sizeof运算符详解:编译期求值、数组指针陷阱与内存对齐

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华