Linux内存占用过高排查手册:OOM Killer、内存泄漏与Swap优化实录
服务器内存耗尽是比磁盘告急更危险的故障——轻则进程被系统强制杀死,重则服务器完全失去响应。本文从诊断工具到根治方案,系统梳理Linux内存问题的完整处置流程。
一、快速查看内存状态
# 最直观的内存概况
free -h
# 输出解读:
# total used free shared buff/cache available
# Mem: 3.8G 2.9G 120M 45M 780M 680M
# Swap: 2.0G 1.2G 800M
#
# available(可用)才是真正可以分配给新进程的内存
# buff/cache是内核缓存,压力大时会自动释放# 实时动态监控(每2秒刷新)
watch -n 2 free -h
# 查看内存使用详细分项
cat /proc/meminfo | grep -E "MemTotal|MemFree|MemAvailable|Cached|SwapTotal|SwapFree"二、定位内存占用进程
# 按内存使用量排序(%MEM列)
ps aux --sort=-%mem | head -20
# top命令中按M键按内存排序
top
# 进入top后按 M 键即可按内存降序排列
# 更详细的进程内存分析(安装smem)
apt install smem -y
smem -s rss -r | head -20 # 按RSS物理内存排序各内存指标含义
| 指标 | 全称 | 含义 | 参考意义 |
|---|---|---|---|
| VSZ | Virtual Size | 进程申请的虚拟内存 | 通常偏大,参考价值低 |
| RSS | Resident Set Size | 进程实际占用的物理内存 | 最重要的参考指标 |
| PSS | Proportional Set Size | 含共享库按比例分摊的内存 | 多进程系统更准确 |
三、OOM Killer:进程被系统杀死的分析
当内存耗尽时,Linux内核的OOM Killer会自动选择并杀死某个进程以回收内存,被杀的进程会在日志中留下记录。
查看OOM Killer日志
# 查看最近的OOM事件
dmesg | grep -i "oom\|killed process\|out of memory" | tail -30
# 或从系统日志查找
grep -i "oom\|killed" /var/log/syslog | tail -30
journalctl -k | grep -i "oom" | tail -30典型OOM日志输出解读
# 示例OOM日志
# [123456.789] Out of memory: Kill process 12345 (php-fpm) score 850 or sacrifice child
# [123456.790] Killed process 12345 (php-fpm) total-vm:2048000kB, anon-rss:1536000kB
# score值越高越容易被杀,评分依据:进程内存占用 / 总内存
# 可以通过调整oom_score_adj降低关键进程被杀概率保护关键进程不被OOM Killer杀死
# 为MySQL设置最低OOM优先级(-1000表示永不被杀)
echo -1000 > /proc/$(pgrep mysqld)/oom_score_adj
# 永久生效(在systemd服务配置中)
# /etc/systemd/system/mysql.service.d/override.conf
[Service]
OOMScoreAdjust=-1000四、内存泄漏的识别与定位
内存泄漏的典型特征:进程内存使用量随时间持续增长,重启后恢复正常,运行一段时间后再次增长。
监控特定进程的内存增长趋势
<code">#!/bin/bash
# 每分钟记录一次指定进程的内存使用
PROCESS="php-fpm"
LOG="/var/log/mem-monitor.log"
while true; do
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
MEM=$(ps aux | grep "$PROCESS" | grep -v grep | awk '{sum+=$6} END {print sum/1024 " MB"}')
echo "$TIMESTAMP - $PROCESS 内存: $MEM" >> "$LOG"
sleep 60
donePHP内存泄漏定位
<code"># 开启PHP慢请求日志(排查内存泄漏来源) # php-fpm配置中添加: slowlog = /var/log/php-fpm-slow.log request_slowlog_timeout = 5s # 查看慢请求日志中的高频调用栈 cat /var/log/php-fpm-slow.log | grep "script_filename" | sort | uniq -c | sort -rn | head -20
五、常见高内存占用场景及解决方案
场景1:MySQL内存占用过高
# 查看MySQL内存配置
mysql -u root -p -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';"
# 调整到合理值(建议为总内存的50%~70%)
# /etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
innodb_buffer_pool_size = 1G # 2G内存服务器建议1G
innodb_buffer_pool_instances = 2
key_buffer_size = 64M场景2:PHP-FPM子进程过多
# /etc/php/8.1/fpm/pool.d/www.conf
[www]
pm = dynamic
pm.max_children = 20 # 最大子进程数(根据内存调整)
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 8
pm.max_requests = 500 # 每个子进程最多处理500次请求后重启(防内存泄漏)场景3:Node.js内存泄漏
# PM2配置内存超限自动重启
pm2 start app.js --max-memory-restart 500M
# 或在ecosystem.config.js中
{
max_memory_restart: '500M'
}六、Swap配置优化
Swap是物理内存的应急缓冲,配置合理可以防止OOM Killer乱杀进程,但过度依赖Swap会导致性能严重下降(磁盘I/O远慢于内存)。
创建Swap文件
# 创建2GB的Swap文件
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
# 开机自动挂载
echo "/swapfile swap swap defaults 0 0" >> /etc/fstab
# 验证
free -h调整swappiness(关键参数)
# swappiness控制系统倾向于使用Swap的程度
# 0 = 尽量不使用Swap
# 100 = 积极使用Swap
# 默认值60,服务器建议设为10
sysctl vm.swappiness=10
echo "vm.swappiness=10" >> /etc/sysctl.conf七、内存不足的根本解决方案
| 场景 | 建议方案 |
|---|---|
| 单个进程内存泄漏 | 定位泄漏代码并修复,临时设置定时重启 |
| 进程配置不合理 | 调低PHP-FPM子进程数、MySQL缓冲池大小 |
| 业务增长内存不够 | 升级VPS配置(IDC.Net支持在线升配) |
| 多服务同一台机器 | 将数据库迁移到独立服务器,分离资源 |
八、总结
Linux内存问题的排查顺序:free -h 确认状态 → ps aux --sort=-%mem 定位进程 → 查看OOM日志确认是否有进程被杀 → 根据具体进程调整配置。IDC.Net的香港VPS支持在线升级内存配置,业务增长时无需迁移数据,直接升配即可解决内存瓶颈。
版权声明:
作者:后浪云
链接:https://idc.net/help/442734/
文章版权归作者所有,未经允许请勿转载。
THE END
