排查服务器内存问题的时候,我第一个敲下的命令永远是free -h。这个命令在 Linux 系统管理里看着不起眼,但它能回答一个最核心的问题:内存到底够不够用。如果你只是把输出里的两个数字拿出来看,大概率会和内存问题的真相擦肩而过。今天这篇就是纯实战向的 free 命令内容,从基础参数讲到底层原理,再配合真实排查场景,把常见坑一个个掰开说清楚。
我见过不少新人,一看free输出里 used 占了很大比例就慌了,急着找进程杀、重启服务,结果折腾一圈发现问题根本不在那儿。Linux 的内存管理机制和 Windows 那一套完全是两回事,理解 free 的输出,先要理解内核怎么看待"空闲内存"。这篇文章适合刚入门的运维、做后端开发的工程技术人员,也适合所有被"内存告警"吓过的朋友。
1. 先搞清楚 free 命令到底解决什么问题
1.1 为什么排查内存时第一个想到 free
free命令来自 procps 工具包,它的核心功能只有一个:读取/proc/meminfo这个虚拟文件里的内存统计信息,然后按照人类比较容易阅读的格式打印出来。命令本身不发起任何系统调用去探测内存,它就是一个格式化读取器,所以它的性能开销几乎为零,哪怕在高负载的机器上连续执行也完全没有压力。
这给我们的第一个启发是:free 看到的数据是"内核视角"的内存快照,而不是某个进程的内存占用。当你执行free -h的时候,得到的是整个系统范围内所有 CPU 节点合计的内存状态。它不关心哪个进程吃了多少内存,只告诉你内存这个池子现在有多少水、多少可以马上用、多少被当作缓存占着但可以回收。
传统运维习惯里,登录任何一台 Linux 服务器第一步往往就是先跑一下free -h。原因很简单:CPU 高了你还能靠负载均值慢慢分析,磁盘满了你能靠 df 快速定位,但内存一旦出问题,往往表现为应用直接被内核杀掉(OOM Killer),或者系统开始疯狂交换导致整体卡顿,反应时间非常短。先用 free 大致判断内存水位,是成本最低的起步动作。
1.2 free 与 top、ps、vmstat、htop 的分工
有人会问,我用top也能看到内存,用htop还能看到彩色进度条,为什么还要单独学 free?我举个例子你就明白了。top默认界面里那个KiB Mem行,用的数据源和 free 一样,但呈现方式被压缩在一行里,信息密度太低。真正的区别在于:
top擅长看"过程",比如哪个进程在吃 CPU、哪个进程内存涨得快,适合动态观察;ps aux --sort=-%mem擅长看某一瞬间所有进程的静态快照,可以快速找出内存占用最高的进程列表;vmstat 1擅长看内存换入换出(si/so)和 CPU 等待,适合定位交换带来的性能下跌;free擅长给出一个干净利落、无干扰的整体内存结论,而且非常适合写进脚本做监控判断。
我在实际工作中通常的节奏是:收到内存告警,先free -h看整体水位,如果 available 还充足,那说明告警阈值设置可能不合理,根本不用紧张;若 available 很低,再用ps aux --sort=-%mem找出大户,继续用top或pidstat观察该进程的实时变化。free 永远是第一个命令,因为它能最快帮你确定"问题是否真的存在"。
2. 基础用法与常用参数实战
2.1 不同单位显示与最常用组合
先看最基本的用法,free直接回车,默认以 KB(其实是 KiB,1024 字节)为单位显示,数字大得惊人,基本没有可读性。所以日常操作首选-h参数,让系统自动按人类易读的单位(MiB、GiB)输出。
free -h输出大概是这样的:
total used free shared buff/cache available Mem: 31Gi 12Gi 5.2Gi 312Mi 14Gi 18Gi Swap: 31Gi 0B 31Gi这个输出包含了 Mem(物理内存)和 Swap(交换空间)两大部分。Mem 行里除了总内存和实际使用外,buff/cache 指的是内核用作缓存的内存,available 是"真实可用的内存",这个后面我会重点讲。
参数方面,常用的就这么几个:
| 参数 | 作用 | 使用场景 |
|---|---|---|
-h | 自动以人类易读的单位显示 | 日常查看首选 |
-m | 以 MiB 为单位显示 | 脚本统计、整数计算时用 |
-g | 以 GiB 为单位显示 | 看大内存服务器粗略水位 |
-s 秒数 | 每隔 N 秒刷新一次 | 持续性观察内存变化 |
-c 次数 | 与-s配合指定刷新次数 | 采样一段时间后自动退出 |
-t | 在末尾多显示一行总计 | Mem 与 Swap 合计 |
-w | 宽模式,把 buff/cache 拆成 buffers 和 cache 两列 | 深入分析缓存组成 |
-l | 显示低内存和高内存统计 | 老式 32 位内核环境 |
我自己最常用的命令组合是free -h和free -m。前者看整体情况,后者用于脚本里做数值比较。需要注意一点,-m在较新的 procps 版本里实际含义是 MiB,不是 MB,如果脚本里按 1000 进制去转换,算出来的结果会有约 2% 的偏差。严格要求的场景可以加--si参数强制使用 1000 进制,但绝大多数运维场景用 1024 进制反而更符合内核实际计算方式。
2.2 连续监控与脚本化输出
内存问题往往是"瞬时"的,比如某个定时任务瞬间申请大量内存,你手动跑一次free -h可能根本抓不到峰值。这时候就要用-s连续刷新的功能:
# 每 3 秒刷新一次,总共打印 10 次 free -h -s 3 -c 10这个命令会一直输出直到达到 10 次后自动退出。如果我不想频繁敲命令,直接用 watch 包裹效果更好:
watch -n 2 free -hwatch 的好处是屏幕会原地刷新,不会像-s那样刷屏,适合挂在终端页面实时观察。不过要注意,连续监控时终端里看到的数字是"快照",内存每一瞬间都在变化,遇到主要内存波动要到同一时刻把 free 和进程列表抓下来对比,否则容易误判。
脚本化的时候,我一般只提取关键字段。free 输出的第一行一般以Mem:开头,第二行是Swap:。用 awk 可以直接抓走:
# 提取总内存和可用内存,单位 MiB free -m | awk '/^Mem:/{print "total=" $2, "available=" $7}'这里$7对应 available 列,只有带 available 的新版本才适用。如果是老版本内核或者老 procps 包,输出里根本没有 available 列,脚本需要注意兼容,这个坑我在下面的章节会专门说。
2.3 老版本与新版本输出的差异
free 命令从 procps-ng 3.3.9 版本开始,输出格式有了一个重大变化:新增了 available 列,同时把原来的-/+ buffers/cache行去掉了。如果你维护的是 CentOS 6、Ubuntu 14.04 这样的老系统,执行free -m看到的是下面这种格式:
total used free shared buffers cached Mem: 3210 2980 230 10 180 1800 -/+ buffers/cache: 1000 2210 Swap: 1023 0 1023老格式里没有 available,判断可用内存要看-/+ buffers/cache行的 free 列,也就是"去掉 buffers 和 cached 后真正可以被程序使用的内存"。这个设计容易让人困惑,但它的逻辑是:buffers 和 cached 都是内核可以随时回收来给应用程序用的,所以不应算作"已使用"。
打补丁后的新格式把 buffers 和 cache 归并到 buff/cache 列,又增加了 available 列。available 比"free+buff/cache"更准确,因为它考虑了内核需要保留的一部分内存(比如 page cache 写回前的脏页不能无限回收),是内核专门计算出来的"预估可用值"。日常判断服务器内存够不够用,我只看 available 这一列,这是全文最重要的一句结论。
3. 输出结果深度拆解
3.1 每一列分别代表什么
拿新格式的第一行来说:
total used free shared buff/cache available Mem: 31Gi 12Gi 5.2Gi 312Mi 14Gi 18Gitotal:物理内存总量,等于内核配置的内存减去一部分内核自身保留区域,通常比物理条标称值略小。used:已经被占用的内存量。它的计算公式是used = total - free - buff/cache,这里注意,used 是"扣掉缓存后"的结果,不是进程实际占用的总和。free:完全未被使用的内存,也就是 page 处于 free 状态的页数。这个数字小不代表内存紧张,因为 Linux 内核的理念是"闲着就是浪费",空闲内存会尽量被拿去当缓存。shared:tmpfs(临时文件系统)等共享内存占用的部分。在容器环境里,这个值经常被忽略,但 tmpfs 确实会消耗物理内存。buff/cache:块设备缓冲(buffers)和页面缓存(cache、slab 等)合计。这部分内存貌似被占,实际上在内核需要时可以回收。available:预估的可用内存,公式大致是MemFree + Buffers + Cached + SReclaimable - 内存水位线。这是最贴近"你现在能放心用多少内存"的数字。
Swap 行的含义更直观:total 是交换空间总量,used 是已经换出的页占用的空间,free 是剩余空间。Swap 使用长期接近总量,往往说明物理内存确实吃紧,或者存在内存泄漏。
3.2 buff/cache 的底层原理
很多人对 buff/cache 的认知停留在"这是缓存,可以清理"的层面,但真要理解它,需要聊几句内核的存储机制。
buffers 和 cache 本质上是两类不同的东西。buffers 是块设备缓冲,记录的是直接读写块设备时的元数据或原始块数据,比如你格式化文件系统、读取 superblock 的时候,数据会先经过 buffers。cache 则是文件数据的页面缓存(page cache),进程读取文件时,内核会把文件内容缓存在内存里,下次读同样的内容直接命中内存,速度比磁盘快几个数量级。
这两者并不严格区分,很多情况下会互相转换,所以 procps 在新版本里干脆把它们合并成一个 buff/cache 列。要拆开看,用free -w宽模式:
free -w -h输出里会分成buffers和cache两列。此外,/proc/meminfo里还有 SReclaimable(可回收的 slab 内存)等细节。slab 是内核用来管理对象的内存池,比如文件索引节点(inode)、目录项(dentry)都需要小的对象分配,这部分内存通常也包含在 cache 列统计里。
到这里你应该明白一件事:buff/cache 大并不等于内存不够,它是内核为了让系统跑得更快主动做的"投资"。free 和 available 低而 buff/cache 大,恰恰说明文件读写多、缓存利用率高,通常不是坏事。
3.3 available 列为什么最值得关注
拿前面那个例子来说:total 31Gi,free 只有 5.2Gi,但 available 是 18Gi。如果不懂的人看到 used 12Gi、free 5.2Gi,可能直接判断"内存已经用了 80%",但实际上有 18Gi 可以马上给新程序用。服务器实际的内存压力远没有表面数据那么高。
内核计算 available 时会考虑缓存可回收性:干净的 cache 页可以直接回收,脏页需要先写回磁盘才能回收,正在使用的匿名页(进程堆栈)不可回收。所以 available 比单纯 free+cache 更贴近真实可用量。这也是为什么监控系统、容器调度器(比如 Kubernetes 节点压力驱逐)底层用的都是这个值。
我个人判断生产服务器内存是否健康的经验值是:available 占总内存比例低于 20% 时开始警惕,低于 10% 时属于高危状态。这时候再看 Swap 的使用情况,如果 Swap 也在涨,说明内存压力已经真实传导到交换层,需要尽快处理。
4. 实战场景与问题排查
4.1 场景一:新服务器内存占用率"居高不下"
我接过不少这样的运维求助:新部署的机器,刚开机没有任何业务进程,free -h一看,used 已经占了 60% 以上,问是不是被挖矿程序入侵了。
遇到这种情况,先不要慌。首先执行top -bn1 | head -20看看到底哪些进程占内存。大概率你会发现占用最多的进程名是 mysqld、java、nginx 之类的正常业务进程,或者就根本没几个进程。其次用free -w看 buffers 和 cache 的分布,如果 cache 很大,说明系统在启动过程和文件读取中已经把大量页面缓存加载好,这是正常现象。
真正需要注意的是另一种情况:系统刚启动就 used 很高,进程列表却空空如也。这时候检查一下是不是有进程把内存消耗在共享内存里了,按之前说的看 shared 列,再用ipcs -m检查共享内存段。另外,内核参数vm.nr_hugepages如果配置了大页内存,HugePages 会直接从物理内存里划走,free 命令的 total 会变少,这部分开销 top 的进程列表里也看不出来。我遇到过一次,某台数据库服务器配置了 64 个 1GB 的大页,物理内存直接被吃掉 64G,怎么看怎么"内存不够",实际就是大页配置导致的。
4.2 场景二:判断内存是否真的不够用
云计算环境中经常收到"内存充足已用 95%"的告警,登录进去看 free 输出却是一切正常。这种告警偏差往往是因为监控系统用了错误的指标——它计算的是used / total,而不是基于 available 计算。
正确判断内存够不够用的方法三步走:
- 看 available 绝对值:
free -m看 Mem 行的 available 列,比如只看剩下 500M,那确实紧张; - 看 Swap 使用趋势:连续执行
free -s 5几次,如果 Swap 的 used 值持续攀升,说明物理内存不足,匿名页在持续被换出; - 看内存是否有突发增长:结合进程维度
ps aux --sort=-%mem | head -n 5,看是哪个进程在吞内存。
用 available 判断还有一个好处:它天然考虑了缓存回收的代价。比如你启动一个需要 10G 内存的新应用,available 只有 8G,那内核回收缓存的过程中可能伴随大量磁盘写回,应用启动会明显变慢,但不会直接 OOM。available 低于需求但不触发 OOM 时,表现就是系统响应变慢却不报错,这种"慢但不挂"的状态在排查时最容易被忽略。
4.3 场景三:Java 进程内存占用疯涨
Java 应用是内存问题大户,特别是堆外内存、线程栈、元空间这些部分,用ps看 RSS 和 free 报告的对不上很常见。
排查时我一般先执行:
ps aux --sort=-%mem | head -n 5看 Java 进程的 RES 列,再对比 JVM 的-Xmx参数。如果 JVM 堆最大是 4G,进程 RSS 却涨到了 8G,那说明多出来的部分在堆外:DirectByteBuffer、线程栈、JIT 代码缓存、Metaspace 都算。这时候 free 只能告诉你"系统层面的内存少了",定位具体是哪一块,需要配合jcmd、jstat或者 NMT(Native Memory Tracking)工具。
还有一种情况是连接池、线程池配置太大。曾经有个服务,线程池最大 500 个线程,每个线程栈默认 1M,光线程栈就有 500M,加上连接缓冲,内存涨得飞快。从 free 看到 available 不停往下掉,再通过top -Hp PID看到线程数量暴增,最终定位到配置问题。这类问题不是内核故障,是应用对内存的使用方式出了问题,free 的价值在于快速抛出"内存水位异常"这个信号。
4.4 场景四:做一条简单的内存水位监控脚本
与其等告警后手忙脚乱,不如自己写个脚本定时监控。下面这个小脚本是我常用的模板,逻辑很简单:每 5 分钟检查一次 available 占比,低于 20% 时记录到日志,低于 10% 时发送告警消息。
#!/bin/bash # 内存监控脚本:检查 available 比例,异常时记录与告警 # 需要 root 权限执行,建议配合 crontab 每 5 分钟运行一次 LOG_FILE="/var/log/memory_monitor.log" THRESHOLD_WARN=20 THRESHOLD_CRIT=10 # 读取内存数据,单位 MiB MemTotal=$(free -m | awk '/^Mem:/{print $2}') MemAvailable=$(free -m | awk '/^Mem:/{print $7}') if [ -z "$MemTotal" ] || [ -z "$MemAvailable" ]; then echo "$(date '+%Y-%m-%d %H:%M:%S') free 命令输出解析失败" >> "$LOG_FILE" exit 1 fi UsedPercent=$(( (MemTotal - MemAvailable) * 100 / MemTotal )) echo "$(date '+%Y-%m-%d %H:%M:%S') 内存使用率: ${UsedPercent}% (available: ${MemAvailable}M)" >> "$LOG_FILE" if [ "$UsedPercent" -ge $((100 - THRESHOLD_CRIT)) ]; then echo "CRITICAL: 内存严重不足,当前使用率 ${UsedPercent}%" >> "$LOG_FILE" # 这里可以接企业微信、钉钉或邮件接口 elif [ "$UsedPercent" -ge $((100 - THRESHOLD_WARN)) ]; then echo "WARN: 内存使用率偏高,当前使用率 ${UsedPercent}%" >> "$LOG_FILE" fi配合 crontab 使用:
*/5 * * * * /usr/local/bin/memory_monitor.sh注意脚本里用$7拿到 available 列,老版本 free 没有这一列,脚本会解析失败,这个判断我在脚本里做了兜底。强烈建议在脚本开头做兼容检查,否则老系统上定时任务会静默失败,监控等于白做。
5. 常见问题与避坑经验
5.1 典型问题速查表
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| free 命令不存在 | procps 包未安装 | 根据发行版安装 procps 或 procps-ng |
| 脚本取不到 available 列 | procps 版本过老 | 改用 `free -m |
| used 很高但 available 充足 | 缓存占用正常 | 用free -w分看 buffers 与 cache |
| Swap used 持续上涨 | 物理内存不足或泄漏 | 结合进程 RSS 和 free 趋势定位 |
| 系统内存总量比物理条少 | 内核保留、大页配置 | 检查/proc/meminfo中 HugePages_Total |
| 所有进程内存之和远小于 used | 共享内存、内核 slab 或 DirectMap | 检查 shared 列、slabtop、/proc/meminfo |
| free 显示 total 变少了 | memory hotplug 或内核 memblock 保留 | 检查 dmesg 中的内存信息 |
每个问题的处理方式都不一样,但第一步永远是同一个动作:确认 free 输出里 available 的绝对值。大多数"内存报警"最后都止步于这一步——available 还很健康,报警阈值该调。
5.2 几条独家实战心得
第一条:不要一看到 buff/cache 高就忙着清缓存。sync && echo 3 > /proc/sys/vm/drop_caches这个操作在排查问题时可以临时做一次,但是清完缓存后系统文件读取性能会短时下降,而且下次读写又会重新缓存,治标不治本。我见过有人把 drop_caches 写进定时任务,理由竟然是"让 free 显示好看点",这是对内存管理机制最大的误解。缓存是内核自带的性能优化机制,不是内存泄漏。
第二条:判断内存问题要把 free 和 vmstat 结合起来。vmstat 1里重点关注 si(swap in)和 so(swap out)两列,如果这两列持续非零,说明系统确实在频繁换页,这是内存不足的实锤指标。free 本身不提供这个动态信息,只看静态快照容易被瞬间数值误导。
第三条:容器环境里看 free 要谨慎。在容器内部执行 free,看到的是宿主机的内存总览(部分新内核已经做了隔离),并不代表容器自身的配额。判断容器内存使用应该以 cgroup 的统计为准,比如读/sys/fs/cgroup/memory.current或者用docker stats。如果容器里只有 free 可用,至少要意识到它反映的是宿主机水位,不是容器水位。
第四条:写脚本解析 free 输出时,尽量用-m固定单位,同时注意 procps 版本差异。我踩过最典型的坑是:服务器从 CentOS 7 迁移到 Ubuntu 20.04,免费的输出列数不一样,老脚本里 awk 的$7取不到 available 直接取到了空值,导致监控全部误报。这类问题务必要在代码里做字段存在性校验。
最后再分享一个实用小技巧:如果你需要记录某个时刻的内存快照做故障复盘,可以直接把完整输出落盘:
free -h > /tmp/memory_$(date +%Y%m%d_%H%M%S).log多留几个时间点的快照,排查内存泄漏问题时对比 available 的下降趋势,比单看某个瞬间有用得多。内存问题很少是"一下子炸掉"的,大多数情况下是缓慢爬坡,能回看趋势曲线,定位问题的时间能缩短一大半。