尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

第七篇:《内存调优与 OOM 问题排查》

第七篇:《内存调优与 OOM 问题排查》
📅 发布时间:2026/7/6 18:13:46

即使内存管理机制再完善,当物理内存被耗尽时,Linux 内核必须做出抉择:杀掉哪个进程来释放内存? 这个“挥刀”的决定由 OOM Killer 执行。本文深入讲解 OOM Killer 的评分机制、如何保护关键进程不被误杀、如何分析 OOM 日志定位根因,以及 Cgroups v2 下的内存限制策略。同时,介绍内存碎片问题与回收机制,帮你建立完整的内存故障排查能力。

一、OOM Killer:内存耗尽时的“最后裁决”
当系统物理内存(包括 Swap)耗尽时,内核会触发 OOM Killer(Out-Of-Memory Killer) ,选择一个进程杀掉,以释放内存,保证系统核心功能继续运行。

1.1 OOM Killer 的评分机制
OOM Killer 通过为每个进程计算一个 “坏蛋分数”(OOM Score) 来决定杀谁。分数越高,越容易被选中。计算基于以下因素:

查看进程的 OOM 分数:

# 查看所有进程的 OOM 分数cat/proc/*/oom_score|sort-nr|head-20# 查看特定进程的分数cat/proc/1234/oom_score

分数范围 0 到 1000(实际上可能更高),分数最高的进程被选为牺牲品。

1.2 保护关键进程:调整 oom_score_adj
通过调整 /proc//oom_score_adj,可以影响进程的 OOM 分数。

示例:保护 MySQL 不被 OOM Kill

# 查找 MySQL 的 PIDpgrep mysqld# 假设 PID 为 1234# 设置 oom_score_adj 为 -1000(禁止 OOM Kill)echo-1000>/proc/1234/oom_score_adj# 验证cat/proc/1234/oom_score_adj

在 systemd 服务中配置:

在服务单元文件中添加:

ini
[Service]

设置 OOM 保护(值范围 -1000 到 1000)

OOMScoreAdjust=-500
1.3 OOM 日志分析
当 OOM Killer 触发时,内核会输出详细的日志到 dmesg 或 /var/log/messages。这是定位内存问题的第一手资料。

查看 OOM 日志:

# 查看最近的内核 OOM 事件dmesg|grep-i"out of memory"-A20# 或从 systemd 日志中查找journalctl-k|grep-i"out of memory"-A20

典型 OOM 日志解读:

[12345.678]Out of memory: Kill process9876(mysqld)score956or sacrifice child[12345.679]Killed process9876(mysqld)total-vm:12345678kB, anon-rss:8765432kB, file-rss:0kB[12345.680]oom_reaper: reaped process9876, now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB

关键信息:

Kill process 9876 (mysqld):被杀的进程是 MySQL。

score 956:该进程的 OOM 分数极高。

total-vm:进程的总虚拟内存。

anon-rss:匿名内存(进程私有内存)的大小。

如果日志中出现 MEMCG 字样,说明是 Cgroups 层级触发的 OOM(容器内存限制),而非宿主机全局 OOM。

二、内存碎片与回收机制
2.1 内存碎片问题
长期运行的系统会产生内存碎片——物理内存被分割成许多不连续的小块,即使有足够的总空闲内存,也可能无法分配一个大块的连续内存(如需要 2MB 连续内存的大页)。

# 查看内存碎片情况cat/proc/buddyinfo

输出示例:

text
Node 0, zone Normal 1 2 4 8 16 32 64 128 256 512 1024
数字从左到右表示空闲页块的大小(2^order 页)。如果左边的数字很大,右边的数字为 0,说明存在碎片化——有大量小内存块,但没有大块连续内存。

2.2 内存回收触发
当内存低于阈值时,内核会启动回收机制,释放 Page Cache 和 Swap Cache。

# 查看内存回收统计cat/proc/vmstat|grep-E'pgscan|pgsteal'

pgscan_kswapd:kswapd 内核线程扫描的页数。

pgsteal_kswapd:kswapd 成功回收的页数。

如果 pgscan 远大于 pgsteal,说明回收效率低,可能需要增加内存或调整 swappiness。

三、Cgroups v2 内存限制
在容器环境中,内存限制通常通过 Cgroups v2 实现。相比 v1,v2 提供了更一致的内存管理接口。

3.1 Cgroups v2 内存核心文件

3.2 容器内存 OOM 排查
当容器触发 OOM 时,可以用以下命令查看:

# 查看容器的内存限制和使用量cat/sys/fs/cgroup/memory.maxcat/sys/fs/cgroup/memory.currentcat/sys/fs/cgroup/memory.events# 查看 oom 事件计数

如果 memory.events 中的 oom 计数不为 0,说明该容器曾触发 OOM。

3.3 Kubernetes 中的内存 OOM
K8s 中 Pod 因内存超限被杀,会显示 OOMKilled 状态:

kubectl describe pod my-pod

输出中会包含:

text
State: Failed
Reason: OOMKilled
解决方案:

增加 Pod 的 memory.limits。

排查应用内存泄漏(使用 jmap、pprof 等工具)。

优化应用内存使用(如减少缓存、调整 GC 参数)。

四、实战:Java 应用内存泄漏排查
Java 应用是内存问题的“重灾区”,因为它运行在 JVM 中,内存管理涉及 JVM 堆和操作系统内存两个层面。

4.1 典型场景
现象:Java 应用的容器内存持续增长,最终触发 OOMKilled。

排查步骤:

第 1 步:确认是否 JVM 堆内泄漏

# 进入容器kubectlexec-itmy-pod -- /bin/bash# 使用 jcmd 查看堆使用情况jcmd<pid>GC.heap_info# 或使用 jstat 观察 GC 行为jstat-gc<pid>1s

如果 GC.heap_info 显示 Old Gen 持续增长,且 GC 无法回收,说明堆内泄漏。

第 2 步:获取堆转储(Heap Dump)

# 生成堆转储文件jcmd<pid>GC.heap_dump /tmp/heap.hprof# 或使用 jmapjmap -dump:live,format=b,file=/tmp/heap.hprof<pid>

第 3 步:分析堆转储

将 heap.hprof 下载到本地,使用 MAT(Memory Analyzer Tool) 或 JVisualVM 分析。

MAT 的 Leak Suspects Report 可以直接告诉你哪个对象占用了最多的内存,以及它的引用链。

4.2 非堆内存泄漏(Direct Memory / Native Memory)
如果堆内存正常,但容器内存仍然不断增长,可能是 Direct Memory 或 Native Memory 泄漏。

Direct Memory:使用 ByteBuffer.allocateDirect 分配的内存,不受 JVM 堆大小限制。

Native Memory:JNI 调用或底层 C 库分配的内存。

排查 Native Memory 泄漏,可以使用 pmap 观察进程的内存段变化,或使用 jemalloc 的 profiling 功能。

五、内存调优实践

# 临时调整sudosysctlvm.swappiness=10# 永久调整(写入 /etc/sysctl.conf)vm.swappiness=10

5.2 调整 overcommit 策略
/proc/sys/vm/overcommit_memory 控制内核是否允许超额分配内存:

对于数据库服务器,建议设置为 2(严格限制),避免因超额分配导致 OOM。

echo2>/proc/sys/vm/overcommit_memory

配合 overcommit_ratio(默认 50%),总可分配内存为 物理内存 * overcommit_ratio / 100 + Swap。

5.3 使用 numactl 优化 NUMA 内存访问

# 查看 NUMA 拓扑numactl--hardware# 将进程绑定到 NUMA 节点 0 的 CPU 和内存numactl--cpunodebind=0--membind=0./myapp

六、小结
OOM Killer 是内存耗尽的最终防线,通过 oom_score 决定杀谁。用 oom_score_adj 保护关键进程。

Cgroups v2 提供了容器的内存限制:memory.max(硬限制)、memory.high(软限制)。

内存碎片通过 /proc/buddyinfo 查看,碎片严重会影响大页分配。

Java 内存泄漏的常规定位路径:确认堆内泄漏 → 获取堆转储 → 使用 MAT 分析。

相关新闻

  • 3分钟上手LangChain.js:零代码构建AI智能代理的终极指南
  • go2rtc:现代视频流网关的完整解决方案
  • Revoke-Obfuscation与PSScriptAnalyzer对比:哪个更适合你的PowerShell安全需求

最新新闻

  • 每天10分钟学会OceanBase系列(Day 11):备份与恢复,给数据上一道“保险”
  • GPT-4o 多模态应用:5步构建自然场景对话助手,情感识别准确率超85%
  • STM32与KMR221构建高精度电压管理系统
  • OpenCV 4.x 人脸识别:3种算法(EigenFace/FisherFace/LBPH)在ORL数据集上的准确率对比
  • UE4 HISM 与 ISM 性能对比:10万实例下 DrawCall 与帧率实测分析
  • 如何构建多平台音乐聚合系统:洛雪音乐聚合音源技术深度解析

日新闻

  • AI智能体安全防护框架AgentGuard:从原理到实战部署指南
  • KMX63与PIC18F26K40硬件组合及低功耗设计实践
  • 基于YOLO13改进的门体检测模型:C3k2模块与PoolingFormer技术解析

周新闻

  • 基于YOLOv12的番茄成熟度智能检测系统开发
  • 终极RimWorld模组管理指南:用RimSort告别模组冲突烦恼
  • AI Agent框架开发:从理论到实践的完整指南

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号