刚接触Linux系统监控时,很多人会被top命令里那些内存指标搞得一头雾水。VIRT、RES、SHR这些字段到底代表什么?为什么有时候进程显示占用了10GB内存,但系统实际剩余内存还很充足?这就要从Linux的内存管理机制说起了。
Linux采用虚拟内存技术,每个进程都运行在自己的虚拟地址空间中。这就导致我们看到的"内存占用"可能有多个统计维度。比如:
我在排查一个Java应用内存泄漏时,就曾经掉进过RSS的坑。当时发现某个进程RSS高达8GB,差点就要重启服务。后来用smem工具查看USS才发现,实际独占内存只有800MB,其余都是共享库的内存占用。这就是为什么要区分这些指标的关键所在。
VSS(Virtual Set Size)是最"虚胖"的指标,它包含:
查看方法:
bash复制ps -eo pid,vsz,cmd | head -n 5
输出示例:
code复制 PID VSZ CMD
1 128620 /usr/lib/systemd/systemd
2 0 [kthreadd]
3 0 [ksoftirqd/0]
VSS的典型特点是数值大得吓人,但实际参考价值有限。我一般只在以下场景会关注它:
RSS(Resident Set Size)是运维最熟悉的指标,包含:
查看方法:
bash复制top -p $(pgrep -d, nginx)
输出示例:
code复制PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1234 nginx 20 0 462384 57232 2348 S 0.0 0.3 0:00.71 nginx
RSS的最大问题是会重复计算共享内存。比如10个nginx worker进程都使用同一个50MB的共享库,在RSS统计中这50MB会被计算10次。这会导致你误判实际内存压力。
PSS(Proportional Set Size)解决了RSS的重复计算问题:
计算示例:
查看方法:
bash复制smem -P nginx | head -n 3
输出示例:
code复制PID User Command Swap USS PSS RSS
1234 nginx nginx: worker process 0 45212 47865 57232
PSS特别适合评估容器环境的内存占用。我曾经用这个指标发现某K8s Pod实际内存需求只有RSS显示值的60%,成功优化了资源分配。
USS(Unique Set Size)是排查内存泄漏的黄金指标:
查看方法:
bash复制cat /proc/1234/smaps | grep -i private | awk '{sum+=$2} END {print sum}'
在分析一个Python内存泄漏问题时,我通过监控USS发现有个字典对象在不断增长,而RSS指标却因为内存碎片化没有明显变化。这就是USS不可替代的价值。
ps/top组合拳:
bash复制# 查看VSS和RSS
ps -eo pid,user,vsz,rss,cmd --sort=-rss | head -n 5
# 动态监控
top -c -o %MEM
smem高级用法:
bash复制# 按用户统计内存
smem -u
# 输出可读性更好的单位
smem -r -k -c "name uss pss rss"
pmap细节探查:
bash复制# 显示详细内存映射
pmap -x 1234
# 仅显示匿名内存段(常是堆内存)
pmap -x 1234 | grep anon
status文件速查:
bash复制grep -E 'VmSize|VmRSS|VmHWM' /proc/1234/status
smaps文件分析:
bash复制# 统计各内存区域类型
cat /proc/1234/smaps | awk '/Size|Rss|Pss|Private/{print $0}'
内存泄漏监控脚本:
bash复制#!/bin/bash
while true; do
date
grep -E 'Private|Pss' /proc/$1/smaps | awk '
/Private/{priv+=$2}
/Pss/{pss+=$2}
END{printf "USS:%dKB PSS:%dKB\n", priv, pss}'
sleep 5
done
当怀疑有内存泄漏时:
我曾经用这个方法发现过一个Go程序的slice泄漏问题:USS每小时增长2MB,但RSS因为内存池机制没有明显变化。
在Docker/K8s环境中:
配置示例:
bash复制# cgroup v2
echo "500M" > /sys/fs/cgroup/memory.max
优化JVM应用时:
典型问题模式:
对于使用共享内存的进程:
bash复制# 查看所有共享内存段
ipcs -m
# 查看特定共享内存的attached进程
lsof -p $(ipcs -m | awk '/0x/{print $2}')
记得有次排查发现两个不相关的进程通过共享内存通信,导致RSS统计异常,就是通过这些命令发现的。