服务器内存越来越小?3分钟教你定位Linux内存消耗真凶
核心观点:Linux内存"不够用"往往是缓存机制和进程泄漏导致的假象,通过 /proc 文件系统配合 smem、pmap 等工具,3分钟就能精准定位真正的内存消耗来源。
上周五凌晨2点,手机突然响了——线上服务器的内存使用率飙到了95%。SSH连上去敲 free -m,看到可用只有几十MB,心里一紧。
结果你猜怎么着?重启Java服务后,一切正常了。
这种事我相信各位运维朋友没少遇到过。但问题是:每次出问题都靠重启解决,你真的知道内存在被谁吃掉了嘛?
今天聊点实在的,分享几个我压箱底的内存排查思路,下次遇到类似情况,你也能3分钟锁定元凶。
先搞清楚:Linux的内存分配到底是怎么回事
很多人看到 free 命令里 "Available" 很小就觉得服务器要崩了,其实这是Linux的内存分配策略在"作怪"。
Linux会把空闲内存用来做缓存(Page Cache),加速磁盘读写。这些缓存可以在需要时立刻释放给应用程序。所以 看内存不能只看 free,重点要看 available 和 buff/cache。
# 看最关键的内存指标
free -h
# 完整输出参考:
# total used free shared buff/cache available
# Mem: 31Gi 28Gi 1.2Gi 200Mi 4.5Gi 2.8Gi
# Swap: 8.0Gi 0Bi 8.0Gi
重点关注 available 这一列——这才是应用程序真正能用的内存。如果 available 长期低于 1GB,那才需要认真排查。
第一步:用 smem 看清谁在"吃"内存
系统自带的 ps aux 排序不够直观,我推荐用 smem,它能按实际物理内存(PSS)排序,精确反映每个进程的真实内存消耗。
# 安装(CentOS)
yum install smem -y
# 安装(Ubuntu/Debian)
aptinstall smem -y
# 按内存使用量排序,看前20名
smem --sort=pss --numeric |head -20
输出大概长这样:
PID User Command PSS RSS Swap
21234 java /opt/jdk/bin/java -server 2048M 2100M 0M
4567 nginx nginx: worker process 120M 130M 10M
8901 redis redis-server config/redis.conf 95M 100M 0M
2345 mysql /usr/sbin/mysqld 420M 500M 50M
这一眼就能看出来:哪个进程占内存最多,哪些进程的Swap使用量也在飙升。Swap一旦被频繁使用,说明物理内存真的不够用了。
第二步:深入看单个进程的内存详情
发现可疑进程后,用 pmap 和 cat /proc/<PID>/smaps 进一步分析:
# 查看进程内存映射
pmap -x 21234|sort -k3 -n -r |head -20
# 重点关注这几列:
# Address Kbytes RSS Dirty Mapping
# 每个mapping代表一块内存区域
如果发现某个进程RSS(Resident Set Size)持续增长,Swap也在涨,那基本可以判定:这个进程有内存泄漏。
我之前遇到过一个MySQL实例,连接数正常,但内存每周涨2GB,最后撑爆。查了半天发现是 tmp_table_size 设置过大 + 某个慢查询导致内存无法释放。调整参数后解决。
第三步:查系统级内存去向
有时候不只是一个进程的问题,而是整个系统的内存分配策略需要调优。
# 查看内存分配详情
cat /proc/meminfo
# 几个关键指标:
# AnonPages - 匿名映射(进程堆栈等)
# Active(file) - 活跃文件缓存
# Inactive(file) - 非活跃文件缓存(可回收)
# SReclaimable - 可回收的Slab内存
# VmallocUsed - 虚拟内存分配
Slab内存是个容易被忽略的点——内核对象缓存如果过大,也会"偷"走几个GB的内存:
# 查看Slab占用
slabtop -o |head -15
# 重点关注:
# OBJS ACTIVE USE OBJ SIZE SLAB OBJ CACHE SIZE NAME
# 51200 51000 99% 0.19K 6400 12800K ext4_inode_cache
第四步:用 vmstat 监控内存趋势
光看当前状态不够,还要看趋势。有些内存泄漏是缓慢发生的,等你发现时已经晚了。
# 每2秒刷新一次,监控内存和Swap
vmstat210
# 重点看 si/so 列(Swap入/出):
# si: 从Swap读入内存的数据量
# so: 写入Swap的数据量
# 如果这两个值持续不为0,说明内存严重不足
配合这个简单脚本,可以自动告警内存使用率:
#!/bin/bash
# 内存使用率超过85%时告警
THRESHOLD=85
used=$(free|grep Mem |awk'{printf "%.0f", $3/$2 * 100}')
if["$used" -gt "$THRESHOLD"];then
echo"内存告警:使用率 ${used}%,超过阈值 ${THRESHOLD}%"
# 这里接你的告警渠道(钉钉/企业微信/邮件)
fi
说点经验之谈
排查内存问题这些年,有几个坑我踩过也分享出来:
1. JVM堆内存不等于进程总内存
Java进程除了堆(-Xmx设置的),还有MetaSpace、直接内存(DirectBuffer)、本地内存等。JVM监控工具看到的往往只是堆内,真正的内存消耗要结合 pmap 和 OS层面的监控来看。
2. OOM Killer不一定是坏事
Linux会在内存耗尽时自动杀掉最"贪心"的进程来保护系统。收到OOM告警先别慌,查一下是谁触发的:
dmesg|grep -i "out of memory"
dmesg|grep -i "killed process"
3. 监控比排查更重要
最好在问题发生前就发现趋势。我现在线上服务器都配了 Prometheus + node_exporter,内存指标持续采集,趋势图一目了然,比出事再排查高效多了。
总结一下
今天聊的排查思路其实就三步:
- 1.
看整体:free -h 看available,vmstat 看趋势
- 2.
看进程:smem 找最耗内存的进程,pmap 深入分析
- 3.
看系统:/proc/meminfo 查缓存和Slab,slabtop 查内核对象
下次服务器内存报警,别急着重启。先把工具跑一遍,搞清楚谁在消耗,再对症下药。
你们公司服务器内存告警一般怎么处理?是脚本自动重启,还是有更优雅的方案?评论区聊聊,看看大家有什么好招。
标签:#运维 #服务器 #Linux #内存管理 #故障排查 #性能调优 #工程师
分类:服务器运维