🐧 第03章 · 内存与交换
free top vmstat swap
内存与交换¶
内存不够用了怎么办?杀进程还是加交换?这一章讲清内存的分配、缓存与回收机制,以及 free、top、vmstat、swap 的用法。先理解概念,再动手,避免"看 stats 蒙圈、乱杀进程"。
内存的组成¶
Linux 内存不是"用了多少就是多少",free -h 的输出要这样读:
free -h
total used free shared buff/cache available
Mem: 15Gi 7.0Gi 269Mi 285Mi 9.0Gi 8.6Gi
Swap: 0B 0B 0B
- total —— 物理内存总量(本机 15Gi);
- used —— 真正被应用占用的;
- free —— 完全空闲未分配的;
- buff/cache —— 内核把磁盘数据缓冲/缓存在内存,用于加速读文件,可回收;
- available —— 真正可用的内存,看它比看 free 靠谱得多(它把可回收的 cache 也算进去了)。
易错 看到
free只剩几百 M 就慌,其实是buff/cache占了 9G,而available还有 8.6G。Linux 的哲学是"闲置内存不如用来做 cache",所以free显示小、available显示大才是常态。判断内存压力看available,不看free。
进程占用内存:top 与 ps¶
top 是实时交互工具,按下 P 按 CPU 排、M 按内存排,q 退出:
top # 交互模式
top -bn1 | head -20 # -b 批处理,-n1 只输出一次,适合脚本抓快照
ps aux --sort=-%mem # 按内存占用降序(负号倒序)
ps aux --sort=-%mem | head # 只看内存最多的前几行
易错
top里%MEM的单进程内存是"常驻集 (RSS)",不含共享库;带一堆子进程的 Node 应用要按整个进程树累加,别只看一行。ps aux的%MEM是最朴素的内存占比,配合-o可只看想看的列。
ps -eo pid,comm,%mem,rss --sort=-%mem | head # 自定义列查看
CPU 也在消耗:cache 与内存回收¶
很多"内存告急"其实是可回收的 cache,内核有回收机制按需释放。/proc/sys/vm 下的参数控制回收策略:
sysctl vm.swappiness # 0~100,越大越倾向把内存换到 swap
sysctl vm.overcommit_memory # 内存超卖策略
cat /proc/sys/vm/cache_recently_evicted # 观察 cache 回收
vm.swappiness 默认 60,本机 没有配置 swap(Swap: 0B),因此该值对本地回收无意义。若要临时释放缓存(不建议在生产随意执行):
echo 1 > /proc/sys/vm/drop_caches # 清页缓存,需 root(生产谨慎,会短暂变慢)
易错
drop_caches会导致缓存重建,写入大文件的应用会瞬间变慢。它不是"省内存"的常规手段,只在调试时才用。生产上优先检查是不是应用泄漏。
内存动态:vmstat¶
vmstat 看内存的实时变化趋势,比单次快照更能说明问题:
vmstat 1 5 # 每 1 秒采样 1 次,共 5 次
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 269M ... ... 0 0 0 0 ... ... 2 1 97 0 0
关键列:si/so(swap 换入换出,越大内存越吃紧)、free(空闲)、cache、wa(等待磁盘 I/O)。r(运行队列)与 b(阻塞队列)反映 CPU/IO 是否拥挤。
提示
si/so持续非零是内存不足的强信号。本机无 swap,si/so恒为 0;若某天内存打满,.service会直接 OOM 被杀——这也是不做 swap 的代价。
OOM:内存不足时内核怎么处理¶
内存耗尽时,Linux 会选择一个进程杀掉(Out Of Memory killer)。谁被杀了能查:
dmesg -T | grep -i "out of memory" # 查看 OOM 记录
journalctl -k | grep -i "oom" # 内核日志里的 OOM(更完整)
被 OOM 杀掉的常见是占用内存最大的进程,也可能因 oom_score 权重不同而变。看当前各个进程的 OOM 评分:
for p in /proc/[0-9]*/oom_score; do
pid=$(basename $(dirname $p))
echo "$pid $(cat $p) $(cat /proc/$pid/comm)"; done | sort -rn | head
易错 看到"我的进程被杀了"不要只怪内核——根因通常是内存真的不够,或某个进程泄漏。加 swap 能减少 OOM 但不能根治泄漏,先找到谁在吃内存,
ps aux --sort=-%mem排一排。
配置交换(swap)——本机示例¶
本机 Swap: 0B,如果用 Node/MySQL 的内存需求大,加一个 swap 文件是稳妥的兜底(dram 充足时不强制,但建议有):
fallocate -l 2G /swapfile # 创建 2G 交换文件(本机可改成 4G)
chmod 600 /swapfile # 权限收紧,安全要求
mkswap /swapfile # 初始化为 swap
swapon /swapfile # 启用
# 写入 fstab 使其开机自动挂载
echo "/swapfile none swap sw 0 0" >> /etc/fstab
swapon --show # 确认生效
易错 swap 文件权限一定要
600,否则mkswap会警告"insecure permissions"。swapon后建议重启或systemctl restart ...验证。swap 是"缓刑"不是"解决",内存泄漏的应用照样会在 swap 耗尽后被 OOM。
案例:Node 应用占内存过高¶
本机 Node 应用(/var/www/bangdream、端口 3000)如果某天内存涨到临界,先按顺序排查:
ps aux --sort=-%mem | head # 谁吃了内存
pm2/systemctl status bangdream # 看服务状态
journalctl -u bangdream --since "1 hour ago" | tail # 看是否报错或泄漏
# 确认是泄漏后再重启:systemctl restart bangdream
重启只是临时止血;反复出现说明应用代码有内存泄漏,需要去 Node 侧定位(heap 快照、--trace-gc 等)。
易错点¶
- 只盯
free不盯available:内存压力的正确指标是available。 - 内存被
buff/cache占了就以为不够:cache 可回收,不是"白占"。 - 无 swap 的机器内存打满会被 OOM 强杀:重要服务建议配置 swap 兜底。
- 拿到内存异常就
drop_caches:会变慢,先定位泄漏进程。
相关命令¶
free、top、vmstat、ps atop、pmap、fallocate、mkswap、swapon、sysctl、dmesg。