⭐ 核心关注项(日常运维必看)
| 字段 | 示例值 | 含义 | 运维要点 |
|---|
| MemTotal | 3,649 MB | 物理内存总量 | 服务器规格上限,不可变 |
| MemFree | 717 MB | 完全空闲、未被任何进程或内核使用的内存 | ⚠️ 此值低 ≠ 内存不足,Linux 会主动利用空闲内存做缓存 |
| MemAvailable | 1,877 MB | 最关键指标。应用程序实际可用的内存估算值 = Free + 可回收的 Buffers/Cached/SReclaimable | ✅ 判断内存是否充足只看这个 |
| Buffers | 210 MB | 块设备(磁盘)元数据缓存(目录项、inode 等) | 可被内核自动回收,属于"可用"内存的一部分 |
| Cached | 908 MB | 文件内容页缓存。读取过的文件会缓存在这里,再次读取直接命中 | 可被自动回收,属于"可用"内存的一部分 |
| SwapTotal | 2,048 MB | Swap 分区/文件总大小 | 作为物理内存的溢出缓冲 |
| SwapFree | 2,008 MB | 剩余可用 Swap | ⚠️ 若此值持续下降,说明物理内存压力大,正在频繁换页 |
💡 MemAvailable 计算公式
MemAvailable ≈ MemFree + Buffers + Cached + SReclaimable - 内核保留水位
为什么 MemFree 很低但系统正常?
Linux 的设计哲学是"空闲内存就是浪费的内存"。内核会将暂时不用的内存用于文件缓存(Cached/Buffers),加速系统运行。当应用需要内存时,这些缓存会被立即自动回收。因此 MemFree 低是正常的,只有 MemAvailable 低才代表真正的内存紧张。
活跃/非活跃页分类
内核根据页面的访问热度将其分为 Active 和 Inactive 两个 LRU 链表,回收内存时优先从 Inactive 链表开始。
| 字段 | 示例值 | 含义 |
|---|
| Active | 696 MB | 最近频繁访问的页面总量 |
| Inactive | 1,746 MB | 较久未访问的页面总量(优先被回收) |
| Active(anon) | 49 MB | 活跃页中的匿名页(进程堆栈、malloc 分配的内存) |
| Inactive(anon) | 1,293 MB | 非活跃匿名页,是 Swap 的主要候选对象 |
| Active(file) | 647 MB | 活跃的文件缓存页 |
| Inactive(file) | 452 MB | 非活跃文件缓存页,最容易被回收 |
🔍 诊断技巧
- Inactive(anon) 很高 + SwapFree 下降 → 进程内存泄漏或内存不足,匿名页被迫换出
- Active(file) 远高于 Inactive(file) → 文件 I/O 密集,大量文件被反复读取
- Inactive(file) 占大头 → 正常的缓存行为,系统有大量可回收内存
内核内存与脏页
| 字段 | 示例值 | 含义 | 运维要点 |
|---|
| Dirty | 1.8 MB | 已修改但尚未写回磁盘的页缓存 | ⚠️ 持续 > 数百 MB 说明磁盘 I/O 存在瓶颈 |
| Writeback | 0 MB | 正在写回磁盘的页面 | 非零表示内核正在刷盘 |
| AnonPages | 1,315 MB | 所有匿名页总量(进程实际占用的物理内存) | 反映真实进程内存消耗 |
| Mapped | 1,130 MB | 映射到进程地址空间的文件页(mmap) | 共享库、mmap 文件计入此项 |
| Shmem | 18 MB | 共享内存(tmpfs、shmget、POSIX shm) | Docker/tmpfs 使用量大时此值升高 |
| Slab | 413 MB | 内核数据结构缓存总量 | = SReclaimable + SUnreclaim |
| SReclaimable | 347 MB | Slab 中可回收部分(dentry/inode 缓存等) | ✅ 计入 MemAvailable |
| SUnreclaim | 65 MB | Slab 中不可回收部分 | 真正的内核固定开销 |
| KernelStack | 5.6 MB | 内核线程栈占用 | 线程数越多此值越高 |
| PageTables | 23 MB | 页表占用内存 | 进程越多 / 内存越大,此值越高 |
🔍 Slab 深入排查
当 Slab 异常偏高时,可通过以下命令定位具体是哪个内核缓存占用过多:
# 按大小排序查看各 slab 缓存
cat /proc/slabinfo | sort -k3 -rn | head -20
# 或使用 slub 调试(需开启 CONFIG_SLUB_DEBUG)
slabtop -o
🔍 基于 /proc/meminfo 的内存泄漏类型判断
核心思路:/proc/meminfo 本身不能直接"证明"泄漏,但通过字段间的差值计算 + 时序趋势对比,可以准确区分泄漏发生在用户态还是内核态,以及内核态中是 Slab 泄漏还是 Buddy(伙伴系统)泄漏。
一、用户态泄漏 vs 内核态泄漏的判断
关键公式
用户态实际占用 ≈ AnonPages + Mapped + Shmem
内核态实际占用 ≈ Slab + KernelStack + PageTables + VmallocUsed + Percpu
MemTotal = 用户态 + 内核态 + MemFree + Buffers + Cached
判断方法:观察 MemAvailable 下降时,哪个分量在增长
| 现象 | 结论 | 依据 |
|---|
| MemAvailable 持续下降,AnonPages 同步单调递增 | 用户态泄漏 | 匿名页归进程所有,增长说明进程 malloc/mmap 未释放 |
| MemAvailable 持续下降,Slab(尤其 SUnreclaim)同步单调递增 | 内核态 Slab 泄漏 | Slab 是内核对象缓存,SUnreclaim 不可回收,增长即泄漏 |
| MemAvailable 持续下降,AnonPages 和 Slab 均稳定,但 MemFree 持续减少且 Buffers/Cached 未增加 | 内核态 Buddy 泄漏 | 排除了用户态和 Slab 后,剩余内存消耗只能来自伙伴系统直接分配 |
| MemAvailable 下降,但 Buffers/Cached 同步上升 | 非泄漏 | 正常的文件缓存增长,属于可回收内存 |
⚠️ 关键前提
- 必须采集时序数据(至少 30 分钟、每 60s 采样),单次快照无法区分泄漏与正常波动
- 判断前需先执行
echo 2 > /proc/sys/vm/drop_caches 排除缓存干扰,若 drop 后指标回落则非泄漏
二、内核态泄漏细分:Slab 泄漏 vs Buddy 泄漏
确认是内核态泄漏后,通过以下字段组合区分具体类型:
Slab 泄漏的特征
| 判据 | 说明 |
|---|
| SUnreclaim 单调递增 | Slab 中不可回收部分持续增长,是最直接的 Slab 泄漏信号 |
| Slab - SReclaimable 差值扩大 | 即使 SReclaimable 正常波动,只要总量减去可回收部分的净值在涨,就是 Slab 泄漏 |
| VmallocUsed 稳定 | 排除 vmalloc 区域的干扰 |
| PageTables / KernelStack 稳定 | 排除进程数增长带来的正常内核开销 |
补充验证:/proc/slabinfo 中某个缓存的 num_objs 只增不减,可与 meminfo 的 SUnreclaim 趋势交叉印证。
Buddy(伙伴系统)泄漏的特征
Buddy 分配器负责管理物理页帧的直接分配(非 Slab 对象),其泄漏在 meminfo 中的表现较为隐蔽:
| 判据 | 说明 |
|---|
| MemFree 持续下降,但 AnonPages / Slab / Buffers / Cached 均未增长 | 内存被"吃掉"了,但不属于任何已知分类 |
| DirectMap4k + DirectMap2M + DirectMap1G 之和持续增大 | DirectMap 反映内核直接映射的物理页总量,增长说明有物理页被内核直接持有未释放 |
| VmallocUsed 可能同步增长 | 部分 Buddy 分配通过 vmalloc 触发,两者可同时增长 |
| SUnreclaim 稳定 | 排除 Slab 泄漏 |
Buddy 泄漏的本质:内核代码通过 alloc_pages() / __get_free_pages() 直接申请物理页后未调用 free_pages() 释放。这类泄漏不经过 Slab 子系统,因此 SUnreclaim 不会体现,只能通过 "MemTotal 减去所有已知用途后的残差" 来发现。
残差计算公式(Buddy 泄漏量化)
Buddy_疑似泄漏量 = MemTotal - MemFree - Buffers - Cached
- AnonPages - Mapped - Shmem
- Slab - KernelStack - PageTables
- VmallocUsed - Percpu - HugePages相关
- 该残差在正常情况下应接近 0(通常 < 50MB)
- 若残差随时间单调递增,即为 Buddy 泄漏的直接证据
三、决策流程图
MemAvailable 持续下降?
├─ 否 → 无泄漏
└─ 是 → drop_caches 后是否恢复?
├─ 是 → 缓存行为,非泄漏
└─ 否 → AnonPages 是否同步增长?
├─ 是 → ✅ 用户态泄漏(查进程 RSS)
└─ 否 → SUnreclaim 是否同步增长?
├─ 是 → ✅ 内核 Slab 泄漏(查 /proc/slabinfo)
└─ 否 → 计算残差是否持续增长?
├─ 是 → ✅ 内核 Buddy 泄漏(查 alloc_pages 调用点)
└─ 否 → 检查 HardwareCorrupted / CmaUsed 等罕见字段
| 字段 | 示例值 | 含义 | 运维要点 |
|---|
| CommitLimit | 3,921 MB | 系统允许承诺分配的内存上限 | 默认 = RAM × overcommit_ratio/100 + Swap |
| Committed_AS | 2,460 MB | 已承诺分配的内存总量 | ⚠️ 接近 CommitLimit 时新进程可能 OOM |
| VmallocTotal | 34 TB | 内核虚拟内存区域总大小(64位系统) | 理论上限,无需关注 |
| VmallocUsed | 17 MB | 已使用的 vmalloc 区域 | 驱动/模块动态分配的内核内存 |
| Percpu | 8.5 MB | Per-CPU 变量占用 | CPU 核数越多此值越高 |
🔍 Overcommit 策略说明
# 查看当前 overcommit 策略
cat /proc/sys/vm/overcommit_memory
# 0 = 启发式(默认),1 = 始终允许,2 = 严格限制
# 查看 overcommit_ratio(仅策略2生效)
cat /proc/sys/vm/overcommit_ratio
- 策略 0(默认):内核自行判断,大多数场景推荐
- 策略 2:Committed_AS 不允许超过 CommitLimit,适合对 OOM 零容忍的关键业务
内存承诺与虚拟内存
大页内存(HugePages)
| 字段 | 示例值 | 含义 |
|---|
| AnonHugePages | 655 MB | 透明大页(THP)占用的匿名内存 |
| FileHugePages | 63 MB | 透明大页占用的文件缓存 |
| HugePages_Total | 0 | 静态大页总数(未启用) |
| HugePages_Free | 0 | 可用静态大页数 |
| Hugepagesize | 2048 kB | 大页尺寸(默认 2MB) |
| Hugetlb | 0 MB | 静态大页预留的总内存 |
💡 THP vs 静态大页
- 透明大页(THP):内核自动管理,对应用透明。AnonHugePages 有值即表示 THP 在工作
- 静态大页:需手动预留,适用于 DPDK、数据库等对内存延迟敏感的场景
- 争议:THP 在某些数据库负载下可能导致性能抖动,生产环境建议根据业务测试结果决定是否关闭
硬件与底层映射
| 字段 | 示例值 | 含义 | 运维要点 |
|---|
| HardwareCorrupted | 0 | 硬件检测到的内存损坏页数 | 🚨 非零需立即排查硬件故障 |
| DirectMap4k | 384 MB | 内核直接映射区中使用 4K 页的部分 | — |
| DirectMap2M | 3,643 MB | 内核直接映射区中使用 2M 页的部分 | 占比越高,TLB 效率越好 |
| DirectMap1G | 0 MB | 内核直接映射区中使用 1G 页的部分 | 仅大内存服务器支持 |
| CmaTotal / CmaFree | 0 | 连续内存分配器总量/空闲 | 嵌入式/驱动专用,一般服务器为 0 |
🔑 实战速查口诀
内存够不够 → 看 MemAvailable
有没有换页压力 → 看 SwapFree 变化趋势
磁盘 IO 卡不卡 → 看 Dirty + Writeback
内核吃多少 → 看 SUnreclaim + KernelStack + PageTables
会不会 OOM → 看 Committed_AS vs CommitLimit
硬件坏没坏 → 看 HardwareCorrupted
缓存是否健康 → 看 Active/Inactive 比例
🛠️ 常用辅助命令
# 实时监控内存使用
free -h
# 查看各进程内存占用 TOP10
ps aux --sort=-%mem | head -11
# 查看 slab 缓存详情
slabtop -o
# 查看 NUMA 节点内存分布
numastat -m
# 查看内存压力事件(PSI)
cat /proc/pressure/memory
# 查看 zone 信息(内存碎片诊断)
cat /proc/zoneinfo
# 查看 buddy 分配器状态(高阶内存碎片)
cat /proc/buddyinfo