资讯动态

Linux 进程内存排查指南:从 RSS/PSS 到泄漏定位的实用技巧

发布时间:2026/10/17 4:45:53 来源:尧图企业网站定制
1. 先搞清楚进程内存的几个概念再动手很多人一上来就敲top或者ps aux --sort-%mem看到一堆数字就截图走人但我建议你先花三分钟把内存指标的概念理清楚。因为 Linux 的进程内存统计方式和 Windows 不太一样里面藏着几个特别容易踩的坑尤其是 RSS 和共享内存的问题。概念没搞清楚后面排查问题很容易得出错误结论。1.1 常驻内存 RSS 和虚拟内存 VSZ 的区别先看最基础的两个指标VSZ 和 RSS。用生活类比来理解VSZ 相当于你开一家餐厅时“规划的总面积”包括大厅、包间、后厨、仓库哪怕有些区域现在空着没启用也算在内RSS 则是“今天实际摆上了桌椅、正在使用的那部分面积”。具体到 Linux 里VSZVirtual Size进程声明的虚拟地址空间大小单位通常是 KB。它包含进程映射的所有内存区域包括尚未被访问的内存页、共享库、文件映射、可能永远不会用到的预分配空间等。所以 VSZ 往往比实际物理内存占用大得多单纯看 VSZ 没太大参考价值。RSSResident Set Size进程实际驻留在物理内存中的页面大小也就是真正占用了物理内存的部分。RSS 包含进程的私有内存也包含它和别的进程共享的共享库内存页。这里就有第一个大坑ps里的 RSS 会把共享库的内存重复统计。比如你起了 20 个 Nginx worker 进程每个都加载了 glibc、openssl 这些共享库那这 20 个进程的 RSS 加总会把同一份共享库内存算了 20 次。实际上系统物理内存里只存了一份。这就是为什么你统计所有进程 RSS 之和经常会大于 free 命令看到的 used 内存甚至在某些极端情况下超过物理内存总量。1.2 共享内存、私有内存与 PSS/USS 的坑既然 RSS 会重复统计共享内容那更准确的口径就得看 PSSProportional Set Size和 USSUnique Set Size。USS进程独有的物理内存不和其他任何进程共享。这是判断“这个进程真正私有多占地”最靠谱的指标排查内存泄漏时主要看它。PSS把共享内存页按共享进程数量均摊后算到每个进程头上。比如一个 4MB 的共享库被 4 个进程共享那每个进程的 PSS 里包含 1MB。所有进程 PSS 之和非常接近系统真实的物理内存使用量。用餐厅类比RSS 是“这个团队租了几个厅就算几个厅”哪怕两个团队共用一个宴会厅每个团队都把这个厅算成自己独占PSS 是“大宴会厅两家对半分各算一半”USS 则是“自家后厨的专属区域”。实际操作中我判断进程真实内存占用时优先看 PSS 和 USS而不是 RSS。这也是后面讲到的smem工具存在的意义。2. 最常用的三件套ps、top、htop 怎么用工具不在多关键是用对场景。ps适合一次性快照和写脚本top适合临场快速看htop适合需要交互操作的场景。我平时排查问题三个工具都会用到但用法各有侧重。2.1 ps 命令适合一次性快照和脚本化ps是 Linux 下查看进程信息最基础的命令输出是一次性的静态快照。最常用的排查命令是ps aux --sort-%mem ps aux --sort-rss第一条按内存占用百分比降序排列第二条按常驻内存绝对值降序排列。我一般直接用第二条因为百分比会受到整机内存大小影响绝对值更直观。如果嫌输出列太多太乱可以用-o自定义输出字段ps -eo pid,ppid,rss,vsz,%mem,%cpu,comm,args --sort-rss字段含义分别是PID、父进程 PID、常驻内存KB、虚拟内存KB、内存占比、CPU 占比、命令名、完整命令行。这个命令是我在脚本里的主力因为输出格式稳定用awk二次处理非常方便。窄终端环境下args字段会把命令行截断这时可以用comm只看命令名。另外注意rss单位默认是 KB如果要换算成 GB可以直接除以 1048576。ps的局限在于只能看某个瞬间的值。如果你需要观察进程内存随时间的变化比如排查泄漏ps就不够用了得配合循环采样或者用其他工具。2.2 top/htop适合实时观察和排序top是 Linux 自带的实时监控工具。打开后默认按 CPU 排序查看内存情况需要手动处理进入 top 后按M键切换到按内存使用量排序。默认的大小写要注意大写 M 是按内存排序小写 m 是开关内存摘要显示两者完全不同。top 界面里两列需要区分RES和 ps 里的 RSS 等价进程常驻内存。SHR共享内存大小。这部分是共享库、共享内存段等多进程共享同样存在重复统计的问题。%MEMRES 除以物理内存总量算出来的比例。top 有个比较实用的交互操作是按f进入字段配置界面你可以把 PSS 相关字段加进来前提是内核支持也可以通过o加过滤条件只显示特定进程。不过 top 的交互式字段配置对新手不算友好需要一点时间适应。htop是 top 的增强版彩色界面操作更直观。如果发行版没预装安装也很快# Debian/Ubuntu apt install htop # CentOS/RHEL yum install htop在 htop 里按F6选择排序字段选M_PSS或M_RES即可按内存排序用F4可以按进程名过滤。htop 的树状视图F5在查看父子进程内存分布时特别有用能一眼看出哪个子进程是内存大头。不过 htop 默认显示的也是 RES想看 PSS 需要去设置里开启。按F2进入设置在 Display options 里勾选相应的列。说实话如果只是日常确认哪个进程占内存多htop 默认值已经够用。2.3 按内存排序找“大头”的实战命令找内存占用最高的进程我常驻脚本里有一行命令ps -eo pid,user,rss,comm --sort-rss | head -20这行命令能快速列出内存占用前 20 的进程。如果发现某个进程可疑再用pidstat看它的实时变化pidstat -r -p PID 1 5-r表示显示内存信息1 5表示每秒采样一次共采样 5 次。输出里有个%MEM列和RSS列能直观看到内存是不是在持续增长。还有一个容易忽略的命令是free它看的是系统整体内存。排查问题上进程级视角和系统级视角要结合。有时候单个进程 RSS 都不高但系统内存告警那就要看是不是 page cache 占用过高或者有内核线程、slab 内存异常。3. 进阶视角从 /proc 文件系统把内存看穿如果你只满足于“知道谁占了多少内存”前面那些工具就够用了。但做性能排查的人应该明白/proc文件系统才是 Linux 暴露进程内部细节的真正入口。ps、top本质上都是在读取/proc下的信息只是做了格式化展示。3.1 /proc/[pid]/status 里的关键字段每个进程的详细信息都在/proc/[pid]/目录下。查看内存最直接的文件是statuscat /proc/1234/status输出里和内存相关的字段主要有VmSize对应 VSZ虚拟内存大小。VmRSS对应 RSS常驻物理内存。VmData数据段大小包括堆、匿名映射等这个字段排查内存泄漏很有价值。VmStk栈大小。VmExe可执行代码大小。VmSwap进程在 swap 分区中的占用。这个字段很关键很多进程 RSS 不高但系统 swap 爆满就是这里出了问题。VmPeak进程运行历史上达到过的虚拟内存峰值。如果当前 VmSize 不高但 VmPeak 很高说明这个进程曾经申请过大量内存可能后面释放了。读 status 文件有个优势不依赖任何额外工具在最小化系统上也能用。容器排查场景下宿主机上直接cat /proc/[pid]/status是穿透容器视图最直接的方式。具体查看某个进程的内存先找到 PIDpidof nginx # 或者 pgrep -f java.*app.jar然后把 PID 替换进路径即可。3.2 smaps 与 pmap逐段分析进程地址空间如果要知道进程内存具体分布在哪就得看/proc/[pid]/smaps。这个文件记录了进程地址空间每一段映射的详细信息cat /proc/1234/smaps | head -50每一段映射包含 Rss、Pss、Shared_Clean、Shared_Dirty、Private_Clean、Private_Dirty、Swap 等字段。其中我们最关心两个Private_Dirty进程私有的已修改内存页。这是真正由进程独有且可能包含重要数据的部分也是分析内存泄漏的核心指标之一。Pss均摊后的常驻内存。smaps 内容非常长因为进程可能有几百段映射。直接看会眼花缭乱一般用下面的方式聚合分析awk /^Pss:/ {sum $2} END {print sum kB} /proc/1234/smaps这条命令把进程所有映射段的 Pss 加起来得到进程真实的均摊物理内存占用。排查过程中我会对比smaps里各个映射段的 Rss如果发现某个.so库文件映射或者匿名映射段特别大就顺着去查对应的业务逻辑。pmap命令提供类似能力输出更友好pmap -x 1234-x参数显示扩展信息包括 RSS、Dirty 等。最后一行的 total 汇总值可以作为参考。pmap 适合快速定位“进程的哪块内存区域异常”。不过说实话pmap 和 smaps 都是快照式的看泄漏趋势还得靠多次采样对比或者专门的监控工具。3.3 smem把共享内存算清楚的工具如果你需要精确回答“每个进程到底该分摊多少物理内存”我推荐smem。它不是系统自带的需要安装# Debian/Ubuntu apt install smem # CentOS/RHEL yum install smemsmem 的原理是读取每个进程的 smaps把 PSS 统计出来。基础用法# 按 PSS 排序显示所有进程 smem -rk # 只看某个用户的所有进程 smem -u username # 按 USS 排序排查泄漏时用 smem -rk -s uss-r是反转排序大的在前-k是把单位显示成更易读的 KB/MB/GB-s指定排序字段。smem 还有一个-p参数可以生成饼图需要 matplotlib 支持不过实际排查中我很少用图表数字本身已经说明问题了。smem 的缺点是大规模系统上统计较慢因为它要逐个遍历进程的 smaps几千个进程的情况下可能需要几十秒。生产环境慎用测试环境没有问题。4. 实战三种典型场景的处理流程工具聊完了接下来讲实操流程。我梳理了三个最常见的排查场景基本覆盖了工作中绝大部分需求。4.1 线上服务器内存告警怎么排查线上内存告警我的排查顺序是固定的从粗到细第一步看系统整体情况free -h先确认内存是真的不够还是只是 cache 占用高。free -h输出里第二行-/ buffers/cache是关键如果 available 还比较充裕系统其实没有真正的内存压力只是 page cache 占用了内存。这种场景属于“看起来满了实际还能用”不需要立刻处理。第二步看是不是已经触发 swap 或 OOM 风险cat /proc/meminfo | grep -E SwapTotal|SwapFree|Committed_AS vmstat 1 5vmstat 输出里持续偏高的 swap 写入so 列说明内存压力已经传导到磁盘进程可能变慢。如果 dmesg 里有 OOM killer 的记录说明已经发生内核强制杀进程的事件了dmesg | grep -i out of memory | tail -20看到 OOM 记录不要慌先把日志里显示的进程名、PID、当时的内存状态截图保存这是后续分析的第一手证据。第三步按内存排序找出可疑进程ps -eo pid,rss,vsz,comm --sort-rss | head -10对定位出来的进程用前面讲的 pidstat 观察一段时间的趋势pidstat -r -p PID 2 10如果进程内存持续上涨不回落大概率是泄漏或缓存增长如果只是瞬时冲高可能是业务高峰期的正常表现。第四步确认是 swap 占用还是物理内存占用。有些进程 RSS 并不高但 VmSwap 很高说明它被换出到磁盘了这种进程虽然不占物理内存但访问时会变慢需要结合业务优先级判断。4.2 怀疑内存泄漏时怎么验证内存泄漏是排查里最磨人的场景。验证思路不复杂多次采样看内存是否持续单调增长且不随业务波动回落。我的操作流程先选定一个观察窗口一般 5 到 10 分钟。期间用脚本定时采样目标进程的内存for i in $(seq 1 30); do date %H:%M:%S grep -E VmRSS|VmData|VmSwap /proc/1234/status sleep 20 done每 20 秒一次连续 30 次总共 10 分钟。把输出保存到文件后重点看 VmRSS 和 VmData 的走势。VmData 持续增长是一个很强的泄漏信号因为它代表堆内存不断扩张。更精细的做法是看 smaps 里匿名内存的增长for i in $(seq 1 10); do awk /^Pss:/ {sum $2} END {print strftime(%H:%M:%S), sum} /proc/1234/smaps sleep 30 done同时对照业务的请求量或任务量。如果内存增长曲线和业务量正相关且业务量回落后内存不降基本可以确认泄漏。确认泄漏后下一步是定位泄漏点。先看进程是哪种类型Java 进程优先用jstat -gcutil PID 1000看堆内和堆外C/C 进程优先用 valgrind 的 massif 工具配合复现Python/Node 进程则要检查全局容器、日志累积、未释放的对象引用。我一个实际项目里遇到过翻来覆去查不到泄漏源的情况最终是通过比较不同时段的 pmap 输出找到一段持续增长的匿名映射顺着地址范围用/proc/PID/pagemap反查物理页才定位到一个全局缓存 map 里对象不断累积的问题。这个思路分享给大家不要只盯业务代码有时候第三库或框架层的缓存机制才是元凶。4.3 容器环境里查看进程内存要注意什么现在很多业务跑在容器里容器环境看进程内存有特殊性。宿主机上容器的进程就是宿主机上的普通进程ps、top都能看到但有个大坑容器内部看到的/proc/meminfo是宿主机视角的不是容器限制后的视角。在容器里执行free -h看到的是宿主机的内存总量这会让很多人误判。正确做法是宿主机视角直接在宿主机上用ps、top、smem查看注意区分容器里各个进程。容器限额视角查看 cgroup 的内存统计cgroup v2下执行:cat /sys/fs/cgroup/memory.current cat /sys/fs/cgroup/memory.max cat /sys/fs/cgroup/memory.statmemory.current是当前实际用量memory.max是限额memory.stat里有更细的分类统计。如果你用的是 Docker也可以直接用docker stats它会直接显示容器级别的内存限制和使用量比在容器内部看靠谱得多。容器场景还有一个容易忽略的问题进程在容器内看到的/proc/[pid]/status里的 VmRSS 是真实物理内存占用但容器的memory.current除了进程的匿名内存还包含 page cache、tmpfs 等。所以容器内存告警不一定代表某个进程内存泄漏可能是文件读写产生的 cache 涨上去了。此时需要结合memory.stat里的file和anon字段区分grep -E ^anon|^file /sys/fs/cgroup/memory.stat如果是 file 部分占比高说明是缓存类占用可以观察是否随业务回落如果是 anon 部分持续增长才需要严肃对待进程泄漏问题。5. 常见问题与排查技巧实录最后这部分我把平时被问的最多的问题和踩过的坑整理成速查表方便你遇到类似情况时直接对照。5.1 常见误区与对应解析误区实际情况正确做法进程 RSS 总和接近物理内存内存就满了RSS 重复统计共享库总和可能虚高用 smem 看 PSS 总和或者用 free 看 availableVSZ 越大越占内存VSZ 是虚拟地址空间很多是未使用映射关注 VmRSS、VmDatatop 里 %MEM 最高的一定是问题进程高可能是共享库被多次统计结合 USS/PSS 判断再看趋势容器里 free 看到的内存是容器限额容器内看到的是宿主机内存数据用 cgroup 文件或 docker statsswap 使用为 0 就是健康不 swap 也可能是内存即将耗尽看 meminfo 的 available 和 watermark在这些误区里最致命的是第一个。我见过有人把几个进程的 RSS 加起来得出“内存占用 120%”的结论其实系统运行得好好的。所以在对外沟通内存问题时我会统一口径要么报 available要么报 PSS 总和不要报 RSS 总和。5.2 一个小而好用的监控脚本参考这里分享一个我常用的内存排查脚本逻辑简单但非常实用适合临时部署在任何 Linux 机器上#!/bin/bash # 监控指定进程的内存趋势输出到 mem_history.log PID${1:?Usage: $0 pid} INTERVAL${2:-5} COUNT${3:-60} LOGmem_history_${PID}.log echo time VmRSS VmData VmSwap $LOG for ((i0; iCOUNT; i)); do TS$(date %H:%M:%S) RSS$(grep VmRSS /proc/$PID/status 2/dev/null | awk {print $2}) DATA$(grep VmData /proc/$PID/status 2/dev/null | awk {print $2}) SWAP$(grep VmSwap /proc/$PID/status 2/dev/null | awk {print $2}) if [ -z $RSS ]; then echo process $PID not found, exit break fi echo $TS $RSS $DATA $SWAP $LOG sleep $INTERVAL done echo done, check $LOG脚本每一行记录时间戳、常驻内存、堆数据段、swap 占用。跑完后我习惯直接导入 Excel 或用一个简单的 Python 脚本画曲线看 VmRSS 和 VmData 的斜率比看单个数字直观得多。再补一个快速找出整机 PSS 总和的口令确认系统层面“真实”内存占用# 需要 root 权限汇总所有进程 PSS for d in /proc/[0-9]*; do awk /^Pss:/ {sum $2} END {print sum} $d/smaps 2/dev/null done | awk {sum $1} END {print sum kB total PSS}这个命令在进程数很多的机器上会跑得比较慢但结果非常参考价值和 free 的 used 应该比较接近。最后再分享一个小经验排查内存问题不要只看“当前值”一定要看“趋势”。内存问题就像水温升高单看某一次测量永远是“不算太高”但把时间轴拉长趋势本身就是答案。这也是上面所有流程里反复强调连续采样的原因。我曾经排查过一个服务每次重启前 RSS 都是 1.2GB 左右怎么看都正常后来做了 24 小时趋势采样才发现它是从 600MB 线性涨到 1.2GB 后触发 OOM 被杀的问题本质是 6 小时一个周期的缓慢泄漏。单看快照永远发现不了这种问题趋势观测才是内存排查的核心手段。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑