ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Linux内存排查实战:从虚拟内存到OOM Killer的完整方法论

Linux内存排查实战:从虚拟内存到OOM Killer的完整方法论 Linux 下 的 内存 问题十个运维九个头疼。平时看着free输出像模像样一 到业务高峰期就卡顿、掉节点或者半夜被 OOM Killer 的告警短信吵醒。我在服务器上排查过太多内存相关的疑难杂症从进程内存泄漏到内核 slab 异常从 swap 抖动到 JVM 堆外内存膨胀今天把能讲清楚的东西整理成一篇偏实战的笔记。这篇文章不堆理论而是从实际经历出发把 Linux 内存的核心机制、监控命令、故障排查和优化手段串成一套能直接用上的方法论。无论你是运维、后端开发还是刚开始学 Linux 的新人只要你 的业务跑在 Linux 上这里面的内容都值得收藏。1. Linux 内存管理的地基从虚拟内存说起Linux 能同时跑几十个进程还不互相干扰靠的不是物理内存本身而是虚拟内存这层抽象。每个进程都拥有自己独立的地址空间从低地址到高地址依次是代码段、数据段、堆、共享库、栈以及内核空间。进程看到的“内存”是虚拟的这个虚拟地址空间通过 CPU 的 MMU 和内核维护的页表映射到物理页面上。理解这套机制是排查一切内存问题的前提。1.1 进程地址空间与页表的关系每个进程都有独立的页表。32 位系统下地址空间是 4GB内核通常占用 1GB用户空间只剩 3GB64 位系统地址空间大得惊人但真正能用的物理内存依然由硬件和内核共同决定。页表的存在让进程之间天然隔离一个进程没法直接访问另一个进程的物理页面这是系统安全的基础也是共享内存、mmap 这些机制需要特殊处理的原因。你 用 top 看到的 VIRT虚拟内存占用其实是进程的虚拟地址空间总大小它可能非常大——很多语言的运行时会预留大量地址空间。而 RSS常驻内存集才是实际映射到物理内存的页面大小。很多面试题喜欢让人区分这两个概念原因就是实际排查时如果只盯 VIRT 会被严重误导。举个例子一个 Java 进程的 VIRT 可能显示几十 GB但 RSS 可能只有几 GB因为 JVM 启动时预留了堆空间这些空间不实际访问就不会产生物理页面分配。实际运维中我经常看到新人用 top 按 VIRT 排序找内存大户结果找出来的进程根本没问题。正确做法是按 RSS 排序或者更精确地看 PSS按共享页比例分摊后的常驻内存这些在/proc/pid/smaps里都能看到。这个细节在定位问题时特别关键尤其是 Java 和 Node.js 这类带独立运行时的进程。1.2 物理内存、交换分区与页面缓存的分工Linux 的内存使用量永远不可能等于进程 RSS 的简单相加。原因在于页面缓存page cache。内核会把读过的磁盘页缓存在内存里加速后续访问这些页面属于所有进程共享的资源也可以被随时回收。所以你 用free看 used 数值很高时先别急着判死刑可能大部分是 cached。这 就 引 出 一个 经典 问题既然页面缓存能回收为什么还会内存不足因为内存回收有代价。普通页面缓存是干净的直接丢弃就行如果是脏页需要先写回磁盘这个过程会产生 IO 压力。如果系统里大量匿名内存页比如堆、栈需要换到 swap那代价更大因为 swap 在磁盘上读写速度完全没法跟内存比。还有一层容易忽略的内核自身的内存消耗。slab、kmalloc、页表本身都要占物理内存这部分在 free 命令里通常归入 used但很多监控工具不会单独标注。我碰到过一次诡异情况free 显示 used 很高但所有进程 RSS 加起来不到一半最后发现是内核的 dentry cache 和 inode cache 爆了——目录项和文件节点类的 slab 对象占了几十 GB。这种情况用slabtop一看就现原形。1.3 内存回收与 OOM Killer 的触发逻辑Linux 的内存回收机制可以理解为“先软后硬”的阶梯。系统内存吃紧时内核的 kswapd 线程会被唤醒开始回收页面缓存、压缩内存碎片、把匿名页换出到 swap。如果 kswapd 回收速度赶不上分配速度系统就进入直接内存回收direct reclaim路径阻塞进程。再往后如果实在找不到可回收页面OOM Killer 才会启动。OOM Killer 的选人规则很多资料只说“得分最高的被杀”但实际逻辑要复杂得多。内核根据 oom_score 打分得分考虑因素包括进程占用物理内存大小、CPU 时间、运行时间、进程优先级以及是否为 root 进程。数值越高越容易被杀。这里有一个很实用的点/proc/pid/oom_score_adj可以手动调整某个进程的分数范围从 -1000 到 1000。比如数据库这类核心进程可以设置一个负值甚至 -1000避免它被误杀。但注意-1000基本等于对该进程禁用 OOM滥用会让整个系统陷入无进程可杀的僵局。还有一个很多人不知道的点cgroup 有自己的 OOM 触发逻辑。容器场景下当某个 cgroup 内存使用超过限制时内核优先杀这个 cgroup 内的进程而不是全局挑选。这 就 解释 了 为什么 容器 内 的 内存 超限 时 杀 掉 的 常常 是 当前 占用 最高 的 那个 业务 进程 而 不是 全局 得分 最高 的 进程。 分布式 系统 里 如果 容器 频繁 OOM 不 一定 是 宿主 机 内存 不 够 而 是 容器 的 内存 上限 设置 得 太 保守。2. 内存监控命令读懂内核想告诉你的事监控命令是排查内存问题的第一步。但很多人只是机械地执行命令不会解读输出背后的含义。其实 Linux 的各类内存工具输出都非常有讲究读懂它们等于让内核告诉你它现在是什么状态。2.1 free 和 top 的细节盲区先 说free。 新版 free 的 输出 里 有 两个 容易 让 人 迷惑 的 指标 available 和 shared。 available 是 内核 估算 的 可以 分给 新 进程 的 内存 大小 它 包括 可 回收 的 页面 缓存 和 部分 可 换出 的 匿名 页。 判断 系统 是否 内存 不够 看 available 比 看 used 靠谱 得 多。 shared 列 代表 tmpfs、 共享 内存 等 被 多个 进程 共享 的 页面 这里 经常 出现 一个 大坑 你 用 SysV 或 POSIX 共享 内存 接口 分配 了 内存 这些 内存在 free 里 显示 为 shared 但 如果 进程 退出 后 没有 正确 清理 共享 内存 对象 会 残留 占用 一直 不 释放。 排查 这种 问题 用ipcs -m查看 共享 内存 段 用ipcrm手动 清理。top命令 的 内存 部分 有个 更 隐蔽 的问题 top 的 used 值 通常 把 页面 缓存 算 进去 而 在 老 版本 或 某些 精简 系统 上 显示 逻辑 可能 不 一样。 我 习惯 在 排 查 时 先 用top -o %MEM按 内存 占用 排序 快速 定位 进程 然后 用top -p 指定PID单独 观察 目标 进程 的 内存 变化 曲线。 但 top 的 采样 周期 默认 3 秒 如果 进程 内存 是 在 几十 毫秒 内 暴涨 又 回落 靠 top 捕 捉 不 到 这 时候 得 借助 pidstat 或 自己 写 脚本 高频 采样/proc/pid/status里 的 VmRSS 字段。2.2 vmstat 与内存抖动的识别vmstat 1是 我 排 查 内存 问题 时 必 打 的 第一 条 命令。 关注 的 列 就 四 个 siswap 换入、 soswap 换出、 free、 buff/cache。 如果 si/so 持续 大于 0 说明 系统 正在 频繁 使用 swap 内存 压力 已经 到 了 危险 线。 这种 情况 常见 于 内存 配置 不足 或 应用 内存 泄漏 的 晚 期。内存 抖动 在 vmstat 里 的 表现 是 free 忽高忽低、 同时 si/so 周期性 跳动。 我 遇 到 过 一个 案例 某 服务 每隔 10 分钟 内存 占用 骤降 最初 怀疑 是 定时 任务 在 清理 但 用 vmstat 观察 发现 抖动 周期 和 cron 对 不 上 后来 定位 到 是 应用 里 一个 缓存 组件 的 过期 清理 逻辑 写 得 太 粗暴 每次 直接 丢 掉 几 GB 页面 导致 后续 访问 全部 走 磁盘 引发 连锁 的 page fault。 这个 案例 的 教训 是 内存 抖动 不 只 是 内核 的 事 更 多 时候 是 应用 层 的 分配 / 释放 模式 不 合理。补充 一个 新手 常 犯 的 错误 vmstat 中 的 free 列 是 真正 空闲 的 物理 页 但 系统 为了 提升 性能 会 尽量 让 空闲 内存 保持 在 较 低 水平 所以 free 小 本身 不 代表 问 题。 只有 free 持续 下降 且 si/so 持续 非 零 才 说明 内存 压力 真实 存在。2.3 进程级内存追踪pmap 与 smaps当 你 定位 到 某个 进程 内存 占用 异常 时 需要 深入 查看 它的 地址 空间。pmap -x pid能 把 进程 的 每个 映射 段 都 列 出来 包括 代码 段、 堆、 栈、 共享 库、 匿名 映射 等 并 标注 每 段 的 RSS。 这 比 top 给 的 一个 总量 有用 得 多 能 直接 看出 是 堆 占 了 大头 还是 某个 映射 文件 占 了 大头。/proc/pid/smaps提供 更 细 粒度 的 信息 每个 映射 段 都 有 单独的 Rss、 Pss、 Shared_Clean、 Shared_Dirty 等 字段。 特别 是 Pss 它 把 共享 页面 按 引用 进程 数 分摊 是 衡量 单个 进程 “真实 独占” 内存 的 最佳 指标。 排查 共享 内存 使用 场景 时 这个 字段 是 决定性 的 如果 两个 进程 Pss 都 很 低 但 Rss 很 高 说明 它们 共享 了 大 块 内存 这 未必 是 问题 反过 来 如果 Pss 高 而 共享 内存 对象 不 该 存在 就 要 怀疑 是不是 哪里 正常 应该 释放 的 内存 被 意外 共享 了。进程 内存 快速 上涨 时 光 看 smaps 快照 不够 还 需要 连续 采样。 我 写过 一个 简单 的 bash 循环 每隔 0.5 秒 读取 VmRSS 如果 连续 几次 都 在 涨 再 用 pmap 抓 详细 映射。 这种 方式 配合 jstack 或 gdb 能 快速 锁 定 是 代码 里 哪个 模块 在 申请 内存。 等 你 排查 多 了 会 发现 内存 问题 90% 都 是 应用 层 的 分配 模式 问题 真正 内核 的 bug 非常 少见。3. 高频故障的排查实录与处理思路内存 故障 的 场景 无非 那 几类 泄漏、 不足、 抖动、 以及 某些 中间件 或 语言 运行时 的 特殊 行为。 我 把 自己 实际 处理 过 的 案例 拆 开 讲 每个 都 附 上 排查 路径 和 最终 结论 你 下次 碰到 类似 问题 可以 直接 抄 作业。3.1 内存泄漏从 RSS 到 slab 逐步定位内存 泄漏 是 最 典型 的 内存 故障 但 泄漏 的 位置 不同 表现 和 排查 手段 完全 不同。 用户 态 进程 泄漏 的 特征 是 RSS 持续 上涨 即使 请求 量 没有 增加。 排查 时 先用 pidstat 确认 是 哪个 进程 再用 pmap 看 是 堆heap还 是 某个 映射 文件 在 涨。 如果 是 堆 在 涨 Java 系 统 就 用 jmap dump 堆 快照 配合 MAT 或 jprofiler 分析 对象 引用 C/C 系 统 就 用 valgrind 或 AddressSanitizer 跑 一遍 捕捉 未 释放 的 内存。内核 侧 的 “泄漏” 更 隐蔽。 页面 缓存 增长 一般 是 正常 的 但 如果slabtop显示 的 某个 对象 数量 不减 就要 怀疑 是不是 内核 模块 或 文件 系统 层 的 引用 没有 释放。 我 处理 过 一个 真实 案例 某 容器 平台 的 节点 上 反复 操作 Docker 后 节点 的 内存 used 持续 攀升 所有 容器 的 RSS 加起来 都 不 到 used 的 一半。 用 slabtop 一看 是dentry和inode缓存 爆 增 再 深挖 发现 是 某 版本 的 storage driver 在 大量 临时 文件 删除 时 没有 正确 释放 目录项 引用。 这 种 问题 靠 用户 态 工具 根本 看 不 出 来 必须 看 内核 对象。遇到 内核 对象 膨胀 临时 手段 是echo 2 /proc/sys/vm/drop_caches强制 回收 可 回收 部分 但 这 只是 缓解 问题 的 根因 在 内核 模块 或 驱动 里。 优先 升级 相关 组件 版本 否则 只能 通过 降低 相关 操作 频率 来 控制 风险。3.2 内存不足OOM 发生时的第一反应OOM 告警 一来 大多数人 的 第一 反应 是 看 dmesg。 OOM Killer 的 日志 里 会 列出 当时 得分 最高 的 几个 进程 以及 系统 总 内存 状况 这 是 判断 是不是 被 误杀 的 第一 手 资料。 但 dmesg 里 的 进程 列表 只是 当时 的 快照 如果 OOM 发生 在 凌晨 且 进程 已经 重启 日志 可能 被 覆盖 所以 建议 用journalctl -k -f把 内核 日志 持久化 到 文件 免得 事后 找 不 到 线索。OOM 后 的 处置 分 三步。 第一步 判断 是 整体 内存 不足 还是 某 个 进程 异常 看 free 和 进程 RSS 分布 就 能 分 清。 第二步 针对性 处理 整体 不足 就 加 内存、 调整 内核 overcommit 策略、 或者 扩容 集群 进程 异常 就 分析 泄漏 原因 并 修复。 第三步 是 兜底 手段 给 重要 进程 设置 oom_score_adj 负值 或者 在 systemd service 里 加OOMScoreAdjust-500 防止 核心 服务 被 误杀。还 有 一个 大型 现场 经常 被 忽略 的 现象 CPU 软 锁 死 导致 的 内存 分配 卡死 也会 触发 OOM。 某 次 排 查 中 dmesg 显示 大量 “page allocation failure” 但 没有 进程 被 杀 系统 却 已经 不 可 用。 原因 是 内存 碎片 化 严 重 无法 分配 连续 的 大 块 物理 页。 这种 情况 的 处理 是 调整vm.min_free_kbytes预留 一定 连续 内存 或者 检查 是否 有 驱动 存在 大量 高阶 内存 分配 需求。3.3 swap 异常与内存抖动swap 用 满 不 一定 意味着 内存 不 够 但 swap 持续 读写 一定 是 性能 灾难。 这里 要 分清 两个 概念 swap 利用率 高 可能 是 某些 冷 数据 被 换出 后 一直 没 使用 这 正常 但 如果 换入 换出 频繁 说明 内存 压力 大 系统 在 不停 搬运。印象 最 深 的 一个 案例 某个 使用 了 大量 Java 微 服务 的 节点 业务 高峰 时 整机 卡 到 SSH 都 不 响应。 vmstat 看到 si/so 每秒 数百 MB 而 free 明明 还有 几 GB cached。 排查 发现 是 swappiness 默认 值 60 导致 内核 偏向 换 出 匿名 页 而 保留 文件 缓存 加上 JVM 堆 属于 匿名 页 大量 堆 内 对象 在 GC 前 就被 换 走 访问 时 又 换 回 形成 抖动。 最终 的 方案 是 把 该 节点 的vm.swappiness调 到 10 并 给 JVM 进程 添加 了-XX:UseNUMA让 内存 分配 更 贴合 本地 节点。 调整 后 si/so 基本 归 零 卡顿 消失。由此 得 到 一个 经验 swap 抖动 的 优先 排查 顺序 是 先 看 swappiness 配置 再 看 是否 有 进程 在 频繁 fork 比如 脚本 里 每次 请求 都 启 一个 新 进程 最后 再 看 是否 有 大 页 表 或 透明 大 页 的 问题。 因 为 透明 大 页THP在 某些 场景 下 会 增加 内存 碎片 和 分配 延迟 数据库 和 搜索 引擎 这类 性能 敏感 的 服务 通常 建议 关闭 或 只 开启 madvise 模式。3.4 特殊场景JVM 堆外内存与共享内存Java 开发 经常 碰 到 一种 怪象 堆 设 的 是 4G 但 进程 RSS 超过 8G。 这 就是 堆 外 内存off-heap在 作 祟。 JVM 的 堆 外 内存 主要 来自 三 块 直接 内存Direct Buffer、 线程 栈、 Metaspace。 其中 最 容易 失控 的 是 Direct Buffer 因为 它 不 受 堆 大小 限制 由MaxDirectMemorySize单独 控制 Netty、 gRPC 等 框架 使用 的 NIO 都 依赖 它。 排查 时 用jcmd pid VM.native_memory查看 本地 内存 分布 如果 Direct Buffer 持续 增长 需要 检查 是否 有 ByteBuffer 未 释放 或 直接 用 Netty 的 池 化 机制 限制 分配 次数。共享 内存 的 坑 更 隐蔽。 容器 场景 下 Apache Kafka、 Redis 或 自己 写 的 C 程序 使用 mmap 做 共享 内存 时 这些 内存在 宿主 机上 看 是 页面 缓存 或 tmpfs。 如果 你 的 宿 主机 用free看 available 不 高 但 容器 里 怎么 都 分配 不 到 内存 可能 是 共享 内存 把 宿主 机 的 可 回收 页面 都 占 掉 了。 尤其 是 某些 组件 用 tmpfs 当 消息 队列 默认 大小 是 物理 内存 一半 一 不 小心 就 把 宿主 机 内存 吃 掉 大半。 这 种 情况 要 在 应用 层 限制 tmpfs 挂载 大小 并且 在 监控 上 区分 普通 页面 缓存 和 tmpfs 共享 内存 不 然 排 查 时 会 找 错 方向。4. 内存优化与预防体系排查 完 问题 之后 更 重要 的 是 建立 预防 体系。 内存 优化 不是 简单 地 给 服务器 加 内存 而是 让 每 一 字节 都 花 在 刀刃 上 同时 让 系统 在 内存 压力 下 能 优雅 表现。4.1 内核参数 swappiness、overcommit 的设置逻辑vm.swappiness控制 内核 在 内存 压力 时 更 倾向 回收 文件 缓存 还是 换 出 匿名 页。 默认 60 对 桌面 和 通用 服务器 是 平衡 值 但 对 内存 敏感 的 应用 需要 调整。 数据库 类 服务 建议 10 以下 Java 服务 建议 10 到 20 这样 能 尽量 保留 堆 内存 避免 无谓 的 换入 换出。 需要 注意 的 是 swappiness 调 低 后 如果 目录 缓存 被 大 量 占用 文件 访问 性能 可能 下降 所以 不能 一味 追求 低 数值。vm.overcommit_memory是 另一个 关键 参数 默认 0 表示 内核 做 启发 式 判断 允许 少量 超额 分配。 生产 环境 我 通常 建议 保持 0 不 改 成 1。 因为 设置 成 1 后 内核 完全 不 拒绝 任何 内存 分配 请求 一个 失控 的 进程 可以 把 系统 拖垮 而 设置 成 2 且 比率 不 合适 时 可能 会 导致 正常 服务 分配 不 到 内存。 如果 你 确实 需要 保护 某个 关键 服务 不 受 其他 进程 内存 膨胀 影响 优先 考虑 用 cgroup 限制 而 不是 依赖 overcommit 策略。透明 大 页 的 配置 我 也 建议 检查 一下。/sys/kernel/mm/transparent_hugepage/enabled默认 是 always 对 大数据 组件 如 Hadoop、 ClickHouse 来说 经常 导致 内存 碎片 和 CPU 开销 增加。 不少 公司 的 运维 规范 里 都 会 让 这些 服务 所在 节点 设置 为 never 或 madvise 因为 THP 的 后台 整理 线程 消耗 CPU 而且 大 页 分配 失败 时 的 回退 路径 有 锁 竞争 高 并发 下 会 放大 延迟。4.2 应用启动参数与内存治理实践启动 参数 的 合理 设置 往往 比 内核 参数 更 立竿见影。 Java 服务 的-Xms和-Xmx建议 设 成 相同 值 避免 JVM 动态 扩容 时 触发 不必要 的 GC 和 内存 申请。 容器 里 跑 Java 还 要 注意 设置-XX:MaxRAMPercentage 避免 JVM 默认 使用 容器 所在 宿 主机 的 内存 来 计算 堆 大小 导致 容器 内 直接 OOM。Node.js 服务 的--max-old-space-size、 Python 的 内存 缓存 上限、 Go 的 GOMEMLIMIT 每 个 运行时 都 有 自己 的 内存 表达 方式。 Go 从 1.19 开始 支持 通过GOMEMLIMIT设置 软 上限 配合GOGC调 整 GC 触发 频率 是 治理 Go 服务 内存 占用 的 主要 手段。 关键 原则 是 一 个 服务 的 内存 预算 必须 小于 容器 / 节点 给 它 的 配额 留 出 10% 到 20% 的 缓冲 否则 任何 瞬时 流量 波动 都 可能 触发 OOM。很多 团队 容易 忽略 的 是 本地 缓存 的 治理。 比如 Redis Client side cache、 Hibernate 二级 缓存、 自己 写 的 map 缓存 都 要 设置 大小 上限 和 过期 时间。 我 见 过 一个 服务 内存 高 峰 全靠 一个 忘记 设 上限 的 ConcurrentHashMap 撑 起来 节点 重启 前 缓存 里 有 上百万 个 对象。 这 不 是 技术 难题 而是 开发 纪律 问题。4.3 构建内存监控基线与日常巡检没有 基线 就 没 有 异常 检测。 我 的 建议 是 对 每 台 服务器 建立 7 天 的 内存 使用 基线 包括 进程 RSS、 页面 缓存、 slab、 swap 使用 等 指标。 基线 建立 后 设置 动态 告警 比如 进程 RSS 连续 30 分钟 超过 基线 的 1.5 倍 或者 swap 使用 持续 增长 超 2 小时 就 触发 告警。 这 比 固定 阈值 有效 得 多 因为 不同 业务 的 内存 特点 差异 巨大 固定 阈值 要 么 误报 不断 要 么 漏报 严重。巡检 脚本 我 建议 至少 包含 以下 内容free -m的 available 和 cached、/proc/meminfo中 的 Slab 和 活跃 / 不 活跃 匿名 页、vmstat 1 5的 si/so、 以及 进程 维 度 的 RSS Top 10。 有 条件 的 团队 可以 用 node_exporter 采集 内核 指标 配 上 Prometheus 的 recording rule 把 内存 压力 相关 指标 自动 算 好。有个 细节 想 提醒 监控 系统 本身 也 要 控制 内存 消耗。 我 见过 一些 团队 把 Prometheus 的 TSDB 块 大小 设 得 过于 激进 导致 监控 服务 自己 先 OOM 反而 失 去 了 监控 能力。 监控 系统 的 高 可用 和 被 监控 对象 一样 重要。5. 一些容易被忽略的冷门经验最后 分享 一些 平时 文章 很 少 提 但 实际 排 查 中 非常 有用 的 冷门 经验。 这些 东西 不 在 任何 一本 教材 的 目录 里 但 遇到 了 就 让 人 头 疼。5.1 “为硬件保留的内存太大”的 Linux 变体Windows 用户 经常 遇到 “为 硬件 保留 的 内存 太大” Linux 其实 也 有 类似 现象 只 是 表现 不同。 如果 你 的 服务器 物理 内存 是 64G 但free显示 的 total 只有 60G 先 别 急 着 懷疑 内核 少 报 了 —— 检查 BIOS 里 是否 开启 了 内存 映射 预留 或者 GPU 是否 占用 了 一部分 物理 地址 空间。 另外 内核 启动 参数memXXG会 强制 限制 内核 看到 的 内存 大小 有 些 云 镜像 或 自 定义 内核 会 带 上 这个 参数 导致 内存 凭空 消失。处理 这种 问题 先 看 dmesg 里 的 内存 检测 信息 确认 BIOS 提供 的 e820 表 是否 正确 再 检查/proc/iomem里 的 系统 RAM 段。 多数 情况 下 这 是 硬件 保留 或 驱动 占用 不 需要 特别 处理 但 如果 业务 确实 需要 那 部分 内存 可以 尝试 升级 BIOS、 调整 GPU 显存 预 留 或者 调整 启动 参数。5.2 新装系统的内存配置检查清单每次 新 装 完成 一个 Linux 环境 我 都会 按 固定 顺序 检查 内存 相关 配置第一 防 火 墙 类 的 系统 服务 会 缓存 大量 会话 状态 该 关 的 关 该 限 的 限。第二 检查 是否 启用 了 不需要 的 桌面 组件 或 GUI 库 服务器 环境 下 这 是 纯 浪费。第三 检查 NTP、 日志 服务 的 buffer 大小。 rsyslog 默认 队列 可能 很 小 但 如果 日志 量 大 内存 中 的 队列 会 飞速 增长 及时 调整MainMsgQueueSize可以 避免 日志 服务 自己 成 为 OOM Killer 的 目标。第四 对 新 装 的 数据库 或 中间件 根据 实际 内存 设置 对应 的 buffer pool 大小。 MySQL 的 innodb_buffer_pool_size、 Redis 的 maxmemory、 Kafka 的 heap 和 page cache 预 算 每 个 组件 占 内 存 的 大头 都 要 心里 有 数。这 套 检查 清单 花 不 了 十分钟 但 能 避免 很多 上线 后 才 暴露 的 内存 问题。5.3 内存问题排查中的沟通技巧内存 排查 常常 跨 团队 开发 说 运维 环境 没 配 好 运维 说 开发 代码 有 泄漏 双方 各 执 一词。 我 的 经验 是 排查 过程 中 一定 要 留下 证据 链。 每 一个 结论 都 要 有 命令 输出 或 监控 截图 支撑 比如 “ 进程 RSS 从 3G 涨 到 8G 同时 请求 QPS 没有 变化 所以 排除 流量 原因”。 这种 证据 导向 的 沟通 方式 能 显著 减 少 扯皮。另 一个 实用 技巧 是 复现 问题 时 尽量 在 低峰 期 操作 并且 先 给 目标 进程 设置 oom_score_adj 负值 防止 复现 过程 中 进程 被 OOM Killer 误 杀。 有 一次 我 为了 复现 一个 潜在 泄漏 连续 压 测 一个 小时 期间 有 两次 差点 把 节点 搞 挂 后来 才 想 起 事先 调整 oom_score_adj。 这 个 教训 之后 我 再 也 没 忘 过。排查 内存 问题 没有 银 弹 但 只要 你 对 虚拟 内存、 页面 缓存、 swap、 slab 这 几 个 概念 有 清晰 认识 再 配 上 正确 的 命令 和 证据 链 思路 大 部分 问题 都 能 在 半小时 内 定位 到 根 因。 我 个人 的 体 会 是 内存 排 查 七 分 靠 机制 理解 三 分 靠 工具 你 把 内核 的 内存 行为 摸 透 了 遇到 新 问题 时 自然 知道 从 哪个 方向 找。
返回列表