Linux 内存、NUMA与OOM生产排障完整指南

Linux 内存问题经常被一句“内存占满了”概括,但 used 可能是可回收页缓存,也可能是进程匿名页、Slab、共享内存、HugePage或Cgroup限制。排障必须区分系统总内存、进程视角和容器限制,并沿分配、回收、交换和OOM路径定位。

一、理解内存组成

物理内存
├── 进程匿名页:Heap、Stack
├── 文件页:Page Cache、mmap
├── 内核:Slab、页表、网络缓冲
├── 共享内存
├── HugeTLB/THP
└── 可用、回收中与不可回收

free中的available比单纯free更有意义,它估算不触发明显Swap情况下可提供给新应用的内存。

二、快速基线

free -h
vmstat 1
cat /proc/meminfo
ps -eo pid,ppid,comm,rss,vsz,%mem --sort=-rss | head -30
slabtop -o

同时记录:

  • MemAvailable;
  • Swap使用和换入换出;
  • Active/Inactive匿名页与文件页;
  • Dirty/Writeback;
  • Slab/SReclaimable/SUnreclaim;
  • PageTables;
  • HugePages;
  • PSI Memory;
  • OOM和内核日志。

三、RSS、PSS、USS与VSZ

  • VSZ:虚拟地址空间,不代表实际物理占用;
  • RSS:驻留物理页,共享页会在多个进程重复计算;
  • PSS:共享页按使用者分摊,更适合汇总;
  • USS:进程独占的物理页。
cat /proc/<pid>/status
cat /proc/<pid>/smaps_rollup
smem -tk

不要把所有进程RSS简单相加判断物理内存,因为共享库和共享内存会重复统计。

四、Page Cache不是浪费

Linux使用空闲内存缓存文件,应用需要时可回收。页缓存高而MemAvailable充足通常是正常现象。

危险信号:

  • MemAvailable持续下降;
  • 直接回收增加;
  • major fault升高;
  • Swap持续换入换出;
  • PSI full持续;
  • 应用分配延迟;
  • OOM日志。

不要定时执行 drop_caches 作为“优化”。这会丢失有价值缓存并制造后续I/O洪峰。

五、匿名内存与泄漏

进程Heap、JVM、数据库Buffer、解释器对象通常属于匿名内存。

pmap -x <pid>
grep -E 'Rss|Pss|Private|Anonymous|Swap' /proc/<pid>/smaps_rollup

判断泄漏要看随业务负载归一化后的长期趋势,而不是一次快照。确认:

  • 内存是否在请求结束后回落;
  • 对象数量是否增长;
  • allocator是否保留arena;
  • 线程数是否增长;
  • 缓存是否有上限;
  • 文件映射是否增加。

六、Slab与内核内存

slabtop -o
cat /proc/slabinfo

Slab增长可能来自dentry、inode、网络连接跟踪、文件句柄或内核模块。SReclaimable可能回收,SUnreclaim更需关注。

排查:

  • 大量小文件和目录扫描;
  • 容器层和挂载;
  • conntrack;
  • Socket缓冲;
  • 内核驱动泄漏;
  • 删除但仍持有的对象。

七、Swap的正确认识

Swap不是绝对禁用或绝对启用。数据库和低延迟系统常限制或关闭Swap,通用服务器可用少量Swap吸收冷页,但持续swap-in/out表示压力。

swapon --show
vmstat 1
cat /proc/<pid>/status | grep VmSwap

关注 si/so 速率、存储延迟和PSI。降低swappiness不能解决真实内存不足。

八、内存回收与抖动

内核先回收文件页和可回收Slab,压力更大时扫描匿名页并Swap。Direct Reclaim会让应用线程亲自回收,造成延迟尖峰。

grep -E 'pgscan|pgsteal|allocstall|compact' /proc/vmstat
cat /proc/pressure/memory

PSI区分some和full:full表示所有非空闲任务都因资源压力阻塞,通常对服务延迟影响更明显。

九、OOM Killer

journalctl -k | grep -Ei 'out of memory|oom-killer|killed process'
dmesg -T | grep -Ei 'oom|killed process'

保存:

  • 被杀进程;
  • OOM触发上下文;
  • 系统/节点总内存;
  • Cgroup限制;
  • 进程RSS/Swap;
  • oom_score_adj;
  • 当时工作负载。

重启被杀进程只能恢复服务,不能解释为何触发OOM。

十、Cgroup与容器OOM

容器可能在宿主机仍有大量空闲内存时被Cgroup OOM。

Cgroup v2示例:

cat /sys/fs/cgroup/<group>/memory.current
cat /sys/fs/cgroup/<group>/memory.max
cat /sys/fs/cgroup/<group>/memory.events
cat /sys/fs/cgroup/<group>/memory.stat

关注 oom、oom_kill、high 和 max。Kubernetes中还要区分容器limit、Pod QoS、节点MemoryPressure和驱逐。

十一、内存请求与限制

限制过低导致频繁OOM,过高则降低调度密度并掩盖泄漏。通过历史P95/P99、峰值、启动和故障场景设置请求与限制。

Java、Go、Node.js等运行时需要感知容器限制。JVM Heap不是进程全部内存,还包括Metaspace、Direct Buffer、线程栈、JIT和Native库。

十二、NUMA基础

多路服务器把CPU和内存划分为NUMA节点,访问远端内存延迟更高。

lscpu
numactl --hardware
numastat
numastat -p <pid>

大型数据库、虚拟化和GPU工作负载尤其需要关注CPU、内存和PCIe设备拓扑。

十三、NUMA失衡

症状:

  • 某节点内存耗尽,其他节点仍有空闲;
  • numa_miss/foreign升高;
  • 远端访问多;
  • 延迟在扩容CPU后反而升高;
  • 进程跨节点频繁迁移。

治理:

  • CPU和内存亲和性一起设置;
  • 数据库与虚拟机匹配NUMA;
  • 避免只绑CPU不绑内存;
  • 了解自动NUMA balancing;
  • 用真实负载验证,不盲目禁用。

十四、THP与HugeTLB

Transparent Huge Pages减少页表和TLB压力,但内存整理可能造成延迟。一些数据库建议调整THP模式。

cat /sys/kernel/mm/transparent_hugepage/enabled
grep -E 'AnonHugePages|HugePages' /proc/meminfo

显式HugeTLB需要预留,不可被普通内存随意使用。调整前评估应用、内核版本和启动参数。

十五、内存碎片与Compaction

总空闲内存足够仍可能无法分配高阶连续页。

cat /proc/buddyinfo
cat /proc/pagetypeinfo
grep -E 'compact|allocstall' /proc/vmstat

驱动、HugePage、网络和高阶分配可能受碎片影响。不能只靠重启解决,应分析工作负载和内核行为。

十六、脏页与写回

grep -E 'Dirty|Writeback' /proc/meminfo
sysctl vm.dirty_ratio vm.dirty_background_ratio

脏页过多时应用可能阻塞等待回写,看起来像内存问题,实际根因是存储写入能力不足。结合iostat和进程写入判断。

十七、缓存与tmpfs

tmpfs、/dev/shm和共享内存实际消耗内存,也可能计入容器限制。

df -hT
ipcs -m
findmnt -t tmpfs

浏览器、数据库、消息队列和机器学习程序可能大量使用共享内存。容器默认 /dev/shm 太小也会导致异常。

十八、语言运行时排障

Java

区分Heap、Metaspace、Direct、线程栈和Native;使用GC日志、JFR、jcmd和Heap Dump。生成Dump会消耗磁盘和暂停时间。

Go

查看Heap、Stack、MSpan/MCache和GC目标;使用pprof;注意RSS不会立即归还操作系统。

Python

关注对象引用、C扩展、allocator和多进程复制;使用tracemalloc等工具,但它不覆盖所有Native内存。

十九、故障处置路线

  1. 保存Meminfo、vmstat、PSI、进程PSS和Cgroup事件;
  2. 判断系统OOM还是Cgroup OOM;
  3. 区分匿名页、文件页、Slab、共享内存和HugePage;
  4. 停止非关键负载或限流;
  5. 保护核心服务;
  6. 必要时扩容;
  7. 对进程做Heap/Native分析;
  8. 复现并验证修复。

不要在没有证据时先清缓存或重启全部服务。

二十、监控与上线清单

  • [ ] MemAvailable、Swap、PSI和回收速率受监控;
  • [ ] 进程PSS、Cgroup current/max/events可查询;
  • [ ] Slab、PageTable、共享内存和HugePage有基线;
  • [ ] OOM日志集中并关联业务;
  • [ ] 容器请求/限制基于真实峰值;
  • [ ] JVM等运行时包含Native内存预算;
  • [ ] NUMA、CPU和设备拓扑匹配;
  • [ ] THP/Swap等参数经过压测而非模板复制;
  • [ ] 磁盘回写与内存压力联合监控;
  • [ ] 内存泄漏、节点压力和OOM场景已演练。

二十一、结语

Linux 内存排障要回答“内存被谁、以什么形式、在哪个限制域中占用”。Page Cache高并不等于泄漏,宿主机空闲也不代表容器不会OOM。通过PSS、Cgroup、PSI、Slab和NUMA等多视角建立证据,才能避免用重启和清缓存掩盖真正问题。

参考资料

Copyright © yan-jian.com 2023 - 2026 · Powered by GitBook更新时间: 2026-10-29 20:20:35

results matching ""

    No results matching ""