Linux 内存管理:Page Cache、Swap 与 OOM
Linux 内存管理:Page Cache、Swap 与 OOM
一、Linux 内存管理概述
Linux 内存管理是操作系统内核的重要组成部分,负责物理内存分配、虚拟内存映射、页面缓存、内存回收以及内存不足时的资源管理。
与一些操作系统不同,Linux 会尽可能利用空闲内存作为缓存,以减少磁盘 I/O,提高文件访问效率。因此,系统显示的内存使用量并不完全等于应用程序实际占用的内存。
理解 Linux 内存管理,需要重点掌握三个核心机制:
- Page Cache(页缓存):利用物理内存缓存文件数据,减少磁盘访问。
- Swap(交换空间):将部分匿名内存页面换出到磁盘,在内存压力较大时释放物理内存。
- OOM(Out Of Memory):当内核无法满足内存分配需求时,通过回收资源或终止进程缓解内存不足。
这三个机制共同构成了 Linux 在内存利用率、I/O 性能和系统稳定性之间的平衡机制。
二、Linux 虚拟内存管理
2.1 什么是虚拟内存
Linux 使用虚拟内存机制,为每个进程提供独立的虚拟地址空间。
应用程序访问的地址通常是虚拟地址,而不是直接访问物理内存地址。CPU 通过 MMU(Memory Management Unit,内存管理单元)将虚拟地址转换为物理地址。
虚拟内存主要带来以下优势:
- 进程隔离:不同进程拥有独立的虚拟地址空间。
- 内存保护:通过页表权限控制读、写和执行操作。
- 内存共享:多个进程可以映射同一物理内存区域。
- 按需分配:虚拟地址空间可以先建立映射,在实际访问时再分配物理页面。
- 内存扩展:结合 Swap,可以在一定程度上突破物理内存容量限制。
2.2 虚拟地址空间布局
Linux 进程的虚拟地址空间通常包含以下区域:
| 内存区域 | 作用 |
|---|---|
| Text Segment | 存放程序机器指令,通常具有只读和可执行权限 |
| Data Segment | 存放已初始化的全局变量和静态变量 |
| BSS Segment | 存放未初始化或初始化为零的全局变量和静态变量 |
| Heap | 动态内存分配区域 |
| Memory Mapping | 映射共享库、文件和匿名内存 |
| Stack | 存放函数调用栈、局部变量等 |
可以通过以下命令查看进程的内存映射:
cat /proc/<PID>/maps
例如:
cat /proc/1234/maps
也可以使用:
pmap -x 1234
查看进程各内存映射区域的大小和使用情况。
2.3 内存页与页表
Linux 以页(Page)作为内存管理的基本单位。
在常见的 x86-64 系统中,基础页大小通常为 4 KiB。
查看系统页大小:
getconf PAGE_SIZE
输出:
4096
虚拟内存管理过程中,内核通过页表记录虚拟页面与物理页面之间的映射关系。
当进程访问尚未建立有效物理映射的页面时,CPU 会触发 Page Fault(缺页异常),由内核判断如何处理。
缺页异常并不一定意味着程序发生错误,常见情况包括:
- 首次访问尚未分配物理页面的匿名内存。
- 访问尚未加载到内存的文件页面。
- 触发 Copy-on-Write(写时复制)。
- 访问已经被换出到 Swap 的页面。
只有当访问行为违反内存映射权限或访问非法地址时,才可能产生无法恢复的缺页异常,最终导致进程收到 SIGSEGV 等信号。
三、Page Cache:Linux 页缓存机制
3.1 什么是 Page Cache
Page Cache 是 Linux 内核用于缓存文件数据的主要机制。
当应用程序读取文件时,内核通常会先检查文件对应的数据是否已经存在于 Page Cache 中。
如果缓存命中,内核可以直接从内存提供数据,避免磁盘 I/O。
如果缓存未命中,内核需要从存储设备读取数据,并将其缓存到内存中。
其基本流程如下:
应用程序读取文件
|
v
系统调用
|
v
检查 Page Cache
|
+----+----+
| |
缓存命中 缓存未命中
| |
v v
读取内存 读取磁盘
| |
| v
| 写入缓存
| |
+----+----+
|
v
返回应用程序
Page Cache 的主要作用:
- 减少磁盘读取次数。
- 提高文件读取速度。
- 减少存储设备随机 I/O。
- 缓存文件写入数据。
- 提高系统整体 I/O 吞吐量。
3.2 Page Cache 的工作原理
Linux 通过文件页缓存机制管理文件数据。
当应用程序执行:
read(fd, buffer, size);
内核会根据文件偏移量查找对应的缓存页面。
如果数据已经存在于 Page Cache 中,可以直接从缓存读取。
如果不存在,则需要从底层文件系统和存储设备读取数据。
例如:
cat largefile.bin > /dev/null
第一次读取文件时,可能需要从磁盘加载数据。
再次读取时,如果缓存仍然保留在内存中,就可能直接从 Page Cache 获取数据。
可以通过以下方式观察缓存变化:
free -h
重点关注:
total used free shared buff/cache available
Mem: 31Gi 8Gi 2Gi 300Mi 21Gi 22Gi
Swap: 8Gi 0B 8Gi
其中:
free:完全未使用的物理内存。buff/cache:用于缓冲区和缓存的内存。available:内核估算的可供新应用程序使用的内存。
不能简单地将 free 较低判断为内存不足。Linux 会主动利用空闲内存缓存文件数据,在应用程序需要内存时,可以回收其中一部分缓存。
3.3 Clean Page 与 Dirty Page
Page Cache 中的页面可以分为干净页和脏页。
Clean Page(干净页)
缓存数据与磁盘中的文件内容一致。
如果系统内存不足,干净页通常可以直接回收。后续再次访问时,内核可以重新从磁盘读取。
Dirty Page(脏页)
缓存数据已经被修改,但尚未写回磁盘。
例如:
echo "Hello Linux" > test.txt
文件写入操作可能先修改内存中的缓存页面,并将其标记为脏页,之后再由内核异步写回存储设备。
脏页的写回过程:
应用程序写入文件
|
v
修改 Page Cache
|
v
标记 Dirty
|
v
后台写回机制
|
v
磁盘存储
|
v
标记为 Clean
可以通过以下命令查看脏页统计:
grep -E 'Dirty|Writeback' /proc/meminfo
常见字段:
| 字段 | 说明 |
|---|---|
| Dirty | 尚未写回的脏页内存 |
| Writeback | 正在写回磁盘的内存 |
3.4 Page Cache 与内存回收
当系统出现内存压力时,内核会尝试回收不再需要的内存。
对于 Page Cache:
- 干净页通常可以直接丢弃。
- 脏页通常需要先写回磁盘。
- 正在使用或被锁定的页面可能无法立即回收。
因此,Page Cache 并不是永久占用内存的区域,而是可以根据系统负载动态调整的缓存。
查看内存详细信息:
cat /proc/meminfo
重点关注:
MemTotal
MemFree
MemAvailable
Buffers
Cached
Dirty
Writeback
Active
Inactive
3.5 如何清理 Page Cache
Linux 提供了 drop_caches 接口,可以主动释放部分缓存。
执行前建议先同步脏页:
sync
释放 Page Cache:
echo 1 > /proc/sys/vm/drop_caches
释放 dentry 和 inode 缓存:
echo 2 > /proc/sys/vm/drop_caches
同时释放:
echo 3 > /proc/sys/vm/drop_caches
对应关系:
| 值 | 操作 |
|---|---|
1 |
释放干净的 Page Cache |
2 |
释放可回收的 dentry 和 inode |
3 |
同时释放上述两类缓存 |
需要注意:
drop_caches不会释放正在使用的内存。- 不会主动清理脏页。
- 不会释放应用程序正在使用的匿名内存。
- 清理缓存后,后续文件访问可能重新产生磁盘 I/O。
因此,生产环境不建议定期执行 drop_caches 作为常规内存优化手段。
四、Swap:Linux 交换空间机制
4.1 什么是 Swap
Swap 是 Linux 提供的交换空间机制,用于在物理内存压力较大时,将部分内存页面换出到磁盘。
Swap 可以使用独立分区,也可以使用普通文件。
当物理内存不足时,内核可以将暂时不活跃的匿名内存页面写入 Swap,从而释放物理内存供其他进程使用。
需要明确:
Swap 不是物理内存的等价替代品。
磁盘存储的访问延迟通常远高于 RAM。频繁发生 Swap 换入和换出,会显著影响系统性能。
4.2 Swap 的工作原理
Linux 内存管理主要涉及两类页面:
- 文件支持页面(File-backed Pages):例如文件映射和 Page Cache。
- 匿名页面(Anonymous Pages):例如进程堆、栈以及匿名内存映射。
干净的文件支持页面通常可以直接丢弃,后续从文件重新读取。
匿名页面没有可直接重新读取的文件作为数据来源,因此在需要回收时,可能需要先写入 Swap。
Swap 的基本工作流程:
系统物理内存压力增加
|
v
内核启动内存回收
|
v
选择可回收的内存页面
|
+----+----+
| |
文件页面 匿名页面
| |
v v
干净页丢弃 写入 Swap
| |
+----+----+
|
v
释放物理内存
|
v
满足新的内存分配
当进程再次访问已经换出的匿名页面时,会触发缺页异常,内核再从 Swap 中读取数据并恢复到物理内存。
这一过程称为 Swap In。
将内存页面写入 Swap 称为 Swap Out。
4.3 Swap 分区与 Swap 文件
Linux 支持两种常见 Swap 形式。
Swap 分区
使用独立磁盘分区作为交换空间。
查看磁盘分区:
lsblk
创建 Swap 分区后,可以使用:
mkswap /dev/sdXn
swapon /dev/sdXn
Swap 文件
使用普通文件作为交换空间。
例如创建 4 GiB Swap 文件:
fallocate -l 4G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
检查:
swapon --show
free -h
需要注意,部分文件系统对 Swap 文件有额外要求,例如 Btrfs 通常需要按照其 Swap 文件规范创建文件,不能直接套用普通文件系统的创建方式。
4.4 Swap 持久化配置
如果希望系统重启后自动启用 Swap,可以修改 /etc/fstab。
例如:
vim /etc/fstab
添加:
/swapfile none swap sw 0 0
执行:
swapon -a
检查:
swapon --show
4.5 vm.swappiness
vm.swappiness 用于控制内核在内存回收过程中使用 Swap 的倾向。
查看:
sysctl vm.swappiness
修改:
sysctl -w vm.swappiness=10
持久化:
vm.swappiness = 10
常见理解:
| 设置趋势 | 行为倾向 |
|---|---|
| 较低 | 降低主动使用 Swap 的倾向 |
| 较高 | 更积极地考虑将匿名页面换出 |
0 |
显著降低 Swap 使用倾向,但不等于绝对禁用 |
需要注意,现代 Linux 内核的内存回收机制比简单的“内存用完才使用 Swap”复杂得多。内核会根据页面活跃程度、回收成本和内存压力综合判断。
因此,不能简单认为 swappiness=1 一定比 swappiness=60 更适合所有服务器。
4.6 Swap 使用情况监控
查看 Swap 总体使用量:
free -h
查看 Swap 设备:
swapon --show
查看内存交换活动:
vmstat 1
重点关注:
| 字段 | 含义 |
|---|---|
| si | 每秒从 Swap 换入的内存量 |
| so | 每秒向 Swap 换出的内存量 |
例如:
procs -----------memory---------- ---swap-- -----io----
r b swpd free buff cache si so bi bo
1 0 0 204800 1024 409600 0 0 2 5
2 1 1048576 102400 1024 204800 512 1024 10 100
如果 si 和 so 长时间持续较高,通常说明系统存在明显的内存交换活动。
但 Swap 已使用量较高并不一定代表当前存在内存压力。某些页面可能在较早之前被换出,之后一直没有再次访问。
判断是否存在 Swap 瓶颈,应结合换入换出速率、内存压力、I/O 延迟和应用响应时间。
五、OOM:Linux 内存不足处理机制
5.1 什么是 OOM
OOM(Out Of Memory)指系统无法满足内存分配需求的状态。
当内核尝试通过内存回收、页面换出等方式释放内存,但仍然无法满足关键内存分配请求时,可能触发 OOM Killer。
OOM Killer 是 Linux 内核中的内存不足处理机制,用于选择并终止部分进程,释放其占用的内存资源。
其主要目标是:
- 避免系统因内存耗尽而完全失去响应。
- 释放足够的内存以恢复系统运行。
- 尽可能选择对系统整体影响较小的进程。
需要注意,OOM 不一定意味着物理内存和 Swap 都完全耗尽。内存分配限制、内存碎片以及特定内存分配条件,也可能导致分配失败。
5.2 OOM Killer 工作流程
当内核遇到无法满足的内存分配请求时,会尝试进行内存回收。
如果回收无法解决问题,内核可能进入 OOM 处理流程:
进程申请内存
|
v
检查内存资源
|
v
尝试内存回收
|
v
是否满足分配?
|
+---+---+
| |
是 否
| |
v v
分配内存 进入 OOM 处理
|
v
计算进程评分
|
v
选择受害进程
|
v
终止进程
|
v
释放内存
OOM Killer 并不是简单地终止内存占用最大的进程,而是根据多个因素计算进程的 OOM 评分。
5.3 OOM Score 与 OOM Score Adj
Linux 为每个进程提供 OOM 评分机制。
查看进程 OOM 评分:
cat /proc/<PID>/oom_score
查看评分调整值:
cat /proc/<PID>/oom_score_adj
oom_score 表示内核计算出的 OOM 选择评分,通常分值越高,被选中的可能性越大。
oom_score_adj 用于调整进程在 OOM 选择中的优先级。
其取值范围:
-1000 ~ 1000
含义:
| 值 | 作用 |
|---|---|
-1000 |
完全禁止该进程被常规 OOM Killer 选中 |
0 |
默认调整值 |
1000 |
最大程度提高被选中的倾向 |
例如:
降低某个进程被 OOM Killer 选中的倾向:
echo -500 > /proc/<PID>/oom_score_adj
提高某个进程被选中的倾向:
echo 500 > /proc/<PID>/oom_score_adj
需要注意:
- 修改其他用户进程的该参数通常需要足够权限。
oom_score_adj=-1000不应随意用于普通业务进程。- 保护过多关键进程可能导致 OOM 时缺少可终止对象,使系统更难恢复。
- 该机制不能代替服务资源限制和内存监控。
5.4 查看 OOM 日志
OOM 事件通常会记录在内核日志中。
查看:
dmesg -T | grep -i -E 'oom|killed process|out of memory'
使用 journalctl:
journalctl -k | grep -i -E 'oom|killed process|out of memory'
典型日志:
Out of memory: Killed process 1234 (java) total-vm:8192000kB, anon-rss:2048000kB
常见字段:
| 字段 | 含义 |
|---|---|
Killed process |
被终止的进程及 PID |
total-vm |
进程虚拟内存总量 |
anon-rss |
进程驻留在物理内存中的匿名页面大小 |
file-rss |
进程驻留在物理内存中的文件映射页面大小 |
排查 OOM 时,应重点关注进程的实际 RSS、内存增长趋势以及是否存在内存泄漏,而不是只看虚拟内存大小。
5.5 OOM 与内存泄漏
OOM 的常见原因包括:
- 应用程序存在内存泄漏。
- 并发请求过多,导致瞬时内存需求过高。
- 数据库缓存或应用缓存设置不合理。
- 容器内存限制过低。
- 系统物理内存不足。
- 内核内存或其他受限内存资源耗尽。
对于长期运行的服务,可以通过监控 RSS 变化判断是否存在持续内存增长。
例如:
ps -eo pid,comm,rss,%mem --sort=-rss | head -20
其中 RSS 表示进程当前驻留在物理内存中的页面大小,单位通常为 KiB。
如果某个进程 RSS 长期持续增长,且业务负载没有相应变化,就需要进一步检查应用程序的内存分配和释放逻辑。
六、Linux 内存回收机制
6.1 什么是内存回收
当系统内存不足时,Linux 内核会尝试回收可用页面,以满足新的内存分配请求。
内存回收主要包括:
- 回收干净的文件缓存页面。
- 将脏页写回存储设备。
- 将部分匿名页面换出到 Swap。
- 回收可释放的内核缓存和其他内存对象。
Linux 内存回收主要分为两种模式。
直接回收(Direct Reclaim)
当进程申请内存但系统可用内存不足时,内核可能要求当前进程参与内存回收。
这种情况下,内存分配请求可能需要等待回收完成,从而增加应用程序延迟。
后台回收(Background Reclaim)
内核通过 kswapd 后台线程进行内存回收。
当空闲内存低于一定水位时,kswapd 会尝试回收页面,使系统维持足够的可用内存。
6.2 kswapd 线程
kswapd 是 Linux 内核中的后台内存回收线程。
查看:
ps -ef | grep kswapd
它主要负责:
- 扫描内存页面。
- 回收可回收的文件缓存。
- 在必要时将匿名页面换出。
- 维持内存水位。
如果系统持续处于内存压力状态,kswapd 可能频繁运行,造成 CPU 和存储 I/O 开销。
6.3 内存水位机制
Linux 使用内存水位机制控制后台回收和内存分配。
常见水位包括:
| 水位 | 作用 |
|---|---|
min |
最低内存水位,表示系统需要高度重视内存压力 |
low |
低水位,通常会触发后台回收 |
high |
高水位,后台回收通常会持续到目标水位附近 |
查看相关信息:
cat /proc/zoneinfo
也可以查看:
cat /proc/buddyinfo
这些信息主要用于分析内核内存区域、水位和物理内存分配情况。
6.4 内存碎片与连续内存分配
物理内存虽然可能存在大量空闲页面,但这些页面不一定连续。
当内核需要分配较大的连续物理内存区域时,可能因为内存碎片而无法满足请求。
Linux 使用伙伴系统(Buddy System)管理物理页面分配。
查看:
cat /proc/buddyinfo
该文件展示不同内存区域中各阶空闲页面的数量。
例如:
Node 0, zone Normal 120 60 30 15 8 4 2 1
每一列代表不同阶数的空闲块数量。阶数越高,对应的连续物理页面数量越多。
对于需要大块连续物理内存的内核分配请求,内存碎片可能成为分配失败的原因。
七、Linux 内存监控与分析
7.1 使用 free 查看内存
free -h
示例:
total used free shared buff/cache available
Mem: 32Gi 12Gi 2Gi 500Mi 18Gi 19Gi
Swap: 8Gi 1Gi 7Gi
重点字段:
| 字段 | 说明 |
|---|---|
| total | 系统总内存 |
| used | 已使用内存,具体统计口径受版本和实现影响 |
| free | 完全空闲内存 |
| shared | 主要表示 tmpfs 等共享内存使用量 |
| buff/cache | 缓冲区与缓存 |
| available | 内核估算的可供新应用使用的内存 |
其中 available 比 free 更适合判断系统是否还有足够的可用内存。
7.2 使用 /proc/meminfo
cat /proc/meminfo
常见字段:
| 字段 | 说明 |
|---|---|
| MemTotal | 可用物理内存总量 |
| MemFree | 完全空闲内存 |
| MemAvailable | 估算可用内存 |
| Buffers | 块设备相关缓冲区 |
| Cached | 文件缓存相关内存 |
| Active | 最近活跃的内存页面 |
| Inactive | 相对不活跃的内存页面 |
| SwapTotal | Swap 总容量 |
| SwapFree | 剩余 Swap 容量 |
| Slab | 内核 Slab 分配器使用的内存 |
| SReclaimable | 可回收的 Slab 内存 |
| SUnreclaim | 不可回收的 Slab 内存 |
| Dirty | 脏页内存 |
| Writeback | 正在写回的内存 |
7.3 使用 vmstat 监控内存
vmstat 1
常见字段:
| 字段 | 说明 |
|---|---|
| r | 等待运行的进程数 |
| b | 不可中断睡眠的进程数 |
| swpd | 已使用 Swap 容量 |
| free | 空闲内存 |
| buff | 缓冲区内存 |
| cache | 缓存内存 |
| si | Swap 换入速率 |
| so | Swap 换出速率 |
| bi | 块设备读取速率 |
| bo | 块设备写入速率 |
如果 si 和 so 持续较高,同时系统响应时间明显增加,可能存在严重内存压力。
7.4 使用 sar 监控内存
安装 sysstat:
# Debian / Ubuntu
apt install sysstat
# RHEL / CentOS
dnf install sysstat
查看内存统计:
sar -r 1
查看 Swap 活动:
sar -W 1
通过持续采集历史数据,可以分析内存使用趋势,而不是只依赖某一时刻的内存快照。
7.5 使用 smem 分析进程内存
普通 ps 命令显示的 RSS 可能重复计算多个进程共享的物理页面。
smem 可以通过 PSS(Proportional Set Size)等指标,更合理地分摊共享内存。
安装:
apt install smem
查看进程内存:
smem -tk
常见指标:
| 指标 | 含义 |
|---|---|
| USS | 进程独占的物理内存 |
| PSS | 独占内存加上按比例分摊的共享内存 |
| RSS | 进程驻留的物理内存总量 |
对于多个进程共享大量动态库或共享内存的场景,PSS 通常比简单累加 RSS 更适合分析整体内存占用。
八、容器环境中的内存管理
容器并不拥有独立的物理内存,而是通过 Linux cgroup 对进程使用的资源进行限制。
Docker、Kubernetes 等容器平台都依赖这一机制管理容器内存。
8.1 Docker 内存限制
例如:
docker run -d \
--name memory-test \
--memory=1g \
--memory-swap=2g \
nginx
其中:
--memory=1g:限制容器内存使用上限为 1 GiB。--memory-swap=2g:限制容器内存与 Swap 总量为 2 GiB。
在该配置下,容器理论上最多可以使用约 1 GiB 的 Swap。
需要注意,Docker 的 --memory-swap 表示内存与 Swap 的总限制,而不是单独的 Swap 容量。实际行为还受到宿主机 Swap 配置、Docker 版本和 cgroup 实现影响。
8.2 cgroup v2 内存指标
在使用 cgroup v2 的系统中,容器内存控制通常通过以下文件实现:
/sys/fs/cgroup/<容器对应路径>/
常见文件:
| 文件 | 作用 |
|---|---|
memory.current |
当前内存使用量 |
memory.max |
内存使用上限 |
memory.high |
内存高水位控制 |
memory.swap.current |
当前 Swap 使用量 |
memory.swap.max |
Swap 使用上限 |
memory.stat |
内存分类统计 |
memory.events |
内存压力和限制事件统计 |
查看:
cat /sys/fs/cgroup/memory.current
cat /sys/fs/cgroup/memory.max
cat /sys/fs/cgroup/memory.stat
cat /sys/fs/cgroup/memory.events
其中 memory.events 可以用于观察容器是否触发内存限制。
8.3 容器 OOM 与宿主机 OOM
容器环境中需要区分两种情况:
容器级 OOM
容器超过自身 cgroup 内存限制时,内核可能触发该 cgroup 范围内的 OOM 处理。
宿主机级 OOM
宿主机整体内存不足时,可能触发全局 OOM Killer,影响宿主机上的多个进程和容器。
查看 Docker 容器状态:
docker inspect <容器名称> \
--format '{{.State.OOMKilled}}'
查看容器内存使用:
docker stats
如果容器频繁出现 OOM,应结合 memory.events、容器日志、应用程序 RSS 和宿主机内存情况判断原因。
九、内存管理常见问题
9.1 Linux 内存占用很高,但应用程序并没有使用那么多内存
这通常是 Page Cache 导致的。
Linux 会利用空闲内存缓存文件数据,因此 free 显示的空闲内存可能较少。
判断系统内存是否紧张,应重点关注:
free -h
以及:
grep -E 'MemAvailable|Cached|Dirty|SwapFree' /proc/meminfo
如果 MemAvailable 仍然充足,通常不需要主动清理缓存。
9.2 为什么关闭 Swap 后系统反而更容易 OOM?
关闭 Swap 后,系统失去了将部分匿名内存页面换出的能力。
当物理内存压力较大时,内核只能依赖其他可回收页面和内存回收机制。
如果匿名内存占用很高,可能更早触发 OOM。
Swap 并不能解决内存不足的根本问题,但在部分场景中可以为系统提供一定的缓冲空间。
9.3 Swap 已经使用很多,为什么系统仍然有大量可用内存?
Swap 中的页面可能是在此前内存压力较大时换出的。
即使后来系统释放了大量物理内存,这些页面也不一定会立即换回。
因此:
- Swap 使用量高不代表当前一定存在内存压力。
- Swap 使用量低也不代表系统一定没有内存压力。
- 应重点关注
si、so是否持续增长,以及应用程序响应时间。
9.4 OOM 为什么会杀死内存占用最大的进程?
OOM Killer 会综合考虑进程内存占用、OOM 调整值以及其他因素。
它并不是简单地选择 RSS 最大的进程。
此外,容器级 OOM 和全局 OOM 的选择范围也可能不同。
9.5 如何判断系统是否正在发生内存抖动?
内存抖动通常表现为:
- Swap 持续换入和换出。
- 磁盘 I/O 明显增加。
- CPU 时间大量消耗在内存回收。
- 应用程序响应延迟增加。
- 系统吞吐量下降。
可以结合以下命令分析:
vmstat 1
sar -W 1
free -h
如果系统持续发生大量 Swap In / Swap Out,且伴随明显的性能下降,应考虑减少应用程序内存占用、调整并发规模或增加物理内存。
十、Linux 内存调优实践
内存调优不应该以追求较低的内存使用率为目标,而应关注系统是否能够在合理的资源消耗下保持稳定运行。
10.1 监控优先
建议建立以下监控指标:
- MemAvailable。
- Page Cache 和 Dirty。
- Swap 使用量及换入换出速率。
- 进程 RSS、PSS。
- 内存回收活动。
- OOM 事件数量。
- 容器内存限制触发情况。
10.2 合理配置 Swap
是否启用 Swap,需要结合系统用途判断。
| 场景 | 建议 |
|---|---|
| 普通服务器 | 可以保留适量 Swap,作为内存压力缓冲 |
| 数据库服务器 | 根据数据库内存管理策略和延迟要求决定 |
| 桌面系统 | 保留 Swap 通常有助于应对内存压力 |
| 容器宿主机 | 结合容器资源限制和运行时策略配置 |
| 高性能计算 | 根据任务延迟和内存需求评估是否使用 |
10.3 避免盲目清理缓存
Page Cache 能够提高文件访问效率。
频繁清理缓存会导致后续文件访问重新产生磁盘 I/O,反而降低性能。
除非是在进行特定的性能测试或故障排查,否则不建议将 drop_caches 纳入日常运维任务。
10.4 合理设置容器内存限制
容器内存限制应根据实际业务负载设置。
限制过低可能导致容器频繁 OOM,限制过高则可能使单个容器占用过多宿主机资源。
建议结合业务峰值、内存增长趋势和安全余量进行配置。
10.5 定位内存泄漏
对于长期运行的服务,应关注进程内存是否持续增长。
可以使用:
ps -eo pid,comm,rss,%mem --sort=-rss | head -20
持续观察进程 RSS。
如果发现内存持续增长,可以进一步使用应用程序自身的内存分析工具、堆分析工具或性能分析器定位问题。
十一、总结
Linux 内存管理并不是简单地将数据装入物理内存,而是通过虚拟内存、Page Cache、Swap、内存回收和 OOM 等机制协调系统资源。
三项核心机制的关系可以概括为:
| 机制 | 核心作用 | 主要目标 |
|---|---|---|
| Page Cache | 缓存文件数据 | 提高文件 I/O 性能 |
| Swap | 将部分匿名页面换出到磁盘 | 缓解物理内存压力 |
| OOM Killer | 终止部分进程释放内存 | 避免系统因内存耗尽而无法继续运行 |
在 Linux 运维和性能分析中,需要建立以下认识:
- 内存使用率高不一定代表内存不足,Page Cache 是正常的内存利用方式。
- Swap 是内存管理机制的一部分,不应简单视为异常。
- OOM 不一定意味着物理内存和 Swap 已经全部耗尽。
- 内存回收可能影响应用程序延迟,需要关注直接回收和后台回收。
- 容器内存限制依赖 cgroup,必须区分容器级 OOM 和宿主机级 OOM。
- 内存调优应以监控数据和实际负载为依据,而不是盲目修改内核参数。
理解这些机制,有助于定位 Linux 服务器内存占用异常、Swap 活动过高、应用程序内存泄漏以及 OOM 等常见问题。
- 点赞
- 收藏
- 关注作者
评论(0)