当前位置:

首页 > 系统应用 > Linux内存管理实战:OOMKiller触发机制与内存泄漏定位

Linux内存管理实战:OOMKiller触发机制与内存泄漏定位

本文目录

    线上服务跑着跑着突然没了,dmesg 里再补上一句 Out of memory: Killed process xxxx,这种场面,运维和后端开发基本都不陌生。不过,OOM Killer 并不是“看谁不顺眼就杀谁”,它背后有一套明确的判定逻辑;内存泄漏也不是凭空发生、毫无线索,从 /proc/mem

    线上服务跑着跑着突然没了,dmesg 里再补上一句 Out of memory: Killed process xxxx,这种场面,运维和后端开发基本都不陌生。不过,OOM Killer 并不是“看谁不顺眼就杀谁”,它背后有一套明确的判定逻辑;内存泄漏也不是凭空发生、毫无线索,从 /proc/meminfo 到 eBPF,整条排查工具链其实是完整的。接下来这篇内容,会把内存管理的排查路径从内核机制一路讲到工程实践,尽量讲透、讲明白。

    Linux内存管理实战:OOMKiller触发机制与内存泄漏定位

    一、OOM Killer 的选择逻辑

    OOM Killer 不是随机杀进程,而是通过 oom_score 给每个进程打分,分数最高的被杀。理解打分逻辑,才能预判哪个进程会被杀、怎么保护关键进程。

    1.1 oom_score 的计算

    # 查看进程的 oom_score
    cat /proc//oom_score

    # 查看 oom_score_adj(可手动调整,范围 -1000 到 1000)
    cat /proc//oom_score_adj

    oom_score 的主要构成:

    因素权重说明RSS(常驻内存)高进程实际占用的物理内存页页表占用中Page table 占用的内存swap 占用中swap 中的页也会计入子进程内存中子进程的内存部分计入父进程oom_score_adj线性偏移-1000 表示永不被杀,1000 表示优先杀

    核心公式简化理解:

    oom_score ≈ (RSS + swap + 页表) 归一化到 0-1000
    oom_score += oom_score_adj

    实际内核实现比这复杂(涉及 oom_badness 函数),但工程上这样理解足够。

    1.2 保护关键进程

    # 保护 SSH 守护进程(防止 OOM 后无法登录)
    echo -1000 > /proc/$(pidof sshd)/oom_score_adj
    # 保护数据库主进程
    echo -1000 > /proc/$(pidof mysqld)/oom_score_adj

    持久化配置(systemd 服务):

    # /etc/systemd/system/mysqld.service.d/override.conf
    [Service]
    OOMScoreAdjust=-1000
    # 创建 override 后重载
    systemctl daemon-reload
    systemctl restart mysqld

    注意:OOMScoreAdjust=-1000 不等于"绝对安全"——如果系统所有进程都被设为 -1000,内核仍会杀分数最高的。这个值是相对偏移,不是绝对保护。

    二、三种 OOM 场景区分

    线上 OOM 不只有一种,混淆场景会导致排查方向完全错误。

    2.1 系统级 OOM

    整个物理内存+swap 耗尽,内核触发全局 OOM Killer。

    # 确认系统级 OOM
    dmesg -T | grep -i "out of memory"
    # 或
    journalctl -k | grep -i "oom"

    典型输出:

    [2026-07-31 10:23:45] Out of memory: Killed process 5678 (ja va) total-vm:8G, anon-rss:6G, file-rss:200M

    2.2 Cgroup 级 OOM

    cgroup 内存限制触发,只杀该 cgroup 内的进程,不影响系统其他进程。

    # 确认 cgroup OOM
    dmesg -T | grep -i "memory cgroup out of memory"

    典型输出:

    [2026-07-31 10:24:01] memory cgroup out of memory: Killed process 9012 (python) total-vm:2G, anon-rss:1.5G

    2.3 进程级 OOM(malloc 失败)

    进程申请内存失败,自己崩溃。这不是内核杀的,是进程自己处理不了 ENOMEM。

    # 检查应用日志
    grep -i "cannot allocate memory|ENOMEM|malloc failed" /var/log/app/*.log

    2.4 三种场景对照表

    维度系统级 OOMCgroup 级 OOM进程级 OOM触发条件物理+swap 耗尽cgroup 内存超限单次 malloc 失败谁来杀内核 OOM Killer内核 OOM Killer进程自己崩溃影响范围全系统仅 cgroup 内单进程日志位置dmesg / journalctldmesg / journalctl应用日志排查重点谁在吃内存cgroup 限制是否合理单次申请大小、overcommit

    三、内存泄漏定位工具链

    OOM 发生后的第一件事是定位"谁在吃内存"。工具链从粗到细:

    3.1 系统级:/proc/meminfo

    # 关键字段
    grep -E "MemTotal|MemFree|MemA vailable|Buffers|Cached|SwapTotal|SwapFree|Slab|SReclaimable|SUnreclaim" /proc/meminfo

    字段解读:

    字段含义排查价值MemA vailable系统可用内存(含可回收缓存)判断是否真的内存不足Buffers/Cached文件缓存高是正常的,可回收SReclaimable可回收 Slabdentry/inode 缓存,可回收SUnreclaim不可回收 Slab持续增长说明内核对象泄漏SwapFree剩余 swap频繁 swap in/out 说明内存压力大

    注意:MemFree 低不代表内存不足。MemA vailable 才是真实可用量。很多人看到 MemFree 只有 200MB 就慌了,实际 Cached 有 10GB 可以随时回收。

    3.2 进程级:smem 和 pmap

    # 安装 smem
    yum install -y smem # CentOS
    apt install -y smem # Ubuntu

    # 按PSS排序显示前20个进程
    smem -rs pss | head -20

    # PSS(Proportional Set Size)比RSS更准确
    # 它把共享内存按比例分摊给每个进程

    RSS vs PSS vs USS 对比:

    指标含义适用场景RSS进程独占+共享库全部粗略看,会高估PSS独占+共享库按比例分摊准确看进程真实占用USS仅进程独占(不含共享)判断进程自己泄漏了多少
    # pmap 看进程内存映射详情
    pmap -x | sort -k3 -n -r | head -20

    # 输出示例:
    # Address Kbytes RSS Dirty Mode Mapping
    # 00007f8a12340000 524288 524288 4096 rw--- [anon]
    # 00007f8a34560000 32768 32768 0 r-x-- libc-2.17.so

    [anon] 段持续增长通常就是泄漏点——匿名内存是 malloc 分配的堆内存。

    3.3 内核级:slabtop

    # 查看内核 Slab 缓存占用
    slabtop -o -s c | head -20

    # 输出示例:
    # Active / Total Objects (% used) : 12345678 / 15000000 (82.3%)
    # Active / Total Slabs (% used) : 234567 / 234567 (100.0%)
    # Active / Total Caches (% used) : 89 / 120 (74.2%)
    #
    # OBJS ACTIVE USE OBJ SIZE SLABS OBJ/SLAB CACHE SIZE NAME
    # 5678901 5678901 100% 0.19K 135212 42 540848K dentry
    # 1234567 1234567 100% 0.66K 22222 55 355552K inode_cache

    dentry 和 inode_cache 高是正常的(文件系统缓存)。如果某个不认识的 cache 持续增长,可能是内核模块泄漏。

    四、eBPF 追踪内存分配

    传统工具只能看"现在占了多少",eBPF 能看"谁在分配、分配了多少次"。

    4.1 bpftrace 追踪 malloc

    # 安装 bpftrace
    yum install -y bpftrace

    # 追踪 libc malloc 调用,统计每个进程的分配次数和总大小
    bpftrace -e '
    uprobe:/lib/x86_64-linux-gnu/libc.so.6:malloc {
    @bytes[comm] = @bytes[comm] + arg0;
    @calls[comm] = @calls[comm] + 1;
    }
    interval:s:5 { print(@bytes); print(@calls); clear(@bytes); clear(@calls); }
    '

    4.2 追踪内核伙伴分配器

    # 追踪 __alloc_pages,统计每个进程的页分配
    bpftrace -e '
    kprobe:__alloc_pages_nodemask {
    @alloc[comm] = @alloc[comm] + 1;
    }
    kprobe:__free_pages {
    @free[comm] = @free[comm] + 1;
    }
    interval:s:10 {
    printf("=== Page alloc/free stats ===n");
    print(@alloc);
    print(@free);
    }
    '

    如果某进程 @alloc 远大于 @free,就是泄漏点。

    4.3 追踪 brk 和 mmap

    # 堆扩展(brk)和大块分配(mmap)分别追踪
    bpftrace -e '
    tracepoint:syscalls:sys_enter_brk {
    @brk[comm] = @brk[comm] + 1;
    }
    tracepoint:syscalls:sys_enter_mmap {
    @mmap[comm] = @mmap[comm] + 1;
    }
    interval:s:10 { print(@brk); print(@mmap); }
    '
    调用场景增长特征brk小块堆分配(<128KB)持续增长=小块泄漏mmap大块分配(>128KB)或文件映射持续增长=大块泄漏或映射未释放

    五、Cgroup v2 内存限制

    线上服务应该用 cgroup v2 限制内存,避免单服务拖垮整机。

    5.1 查看和设置内存限制

    # 查看 cgroup 版本
    mount | grep cgroup
    # cgroup2 on /sys/fs/cgroup type cgroup2 ...

    # 查看 service 的 cgroup 路径
    systemctl show mysqld --property=ControlGroup

    # 设置内存上限(2GB)
    systemctl set-property mysqld MemoryMax=2G
    interval:s:10 {
    printf("=== Page alloc/free stats ===n");
    print(@alloc);
    print(@free);
    }
    '

    如果某进程 @alloc 远大于 @free,就是泄漏点。

    4.3 追踪 brk 和 mmap

    # 堆扩展(brk)和大块分配(mmap)分别追踪
    bpftrace -e '
    tracepoint:syscalls:sys_enter_brk {
    @brk[comm] = @brk[comm] + 1;
    }
    tracepoint:syscalls:sys_enter_mmap {
    @mmap[comm] = @mmap[comm] + 1;
    }
    interval:s:10 { print(@brk); print(@mmap); }
    '
    调用场景增长特征brk小块堆分配(<128KB)持续增长=小块泄漏mmap大块分配(>128KB)或文件映射持续增长=大块泄漏或映射未释放

    五、Cgroup v2 内存限制

    线上服务应该用 cgroup v2 限制内存,避免单服务拖垮整机。

    5.1 查看和设置内存限制

    # 查看 cgroup 版本
    mount | grep cgroup
    # cgroup2 on /sys/fs/cgroup type cgroup2 ...

    # 查看 service 的 cgroup 路径
    systemctl show mysqld --property=ControlGroup

    # 设置内存上限(2GB)
    systemctl set-property mysqld MemoryMax=2G
    # 设置内存软限制(超过时开始回收,1.5GB)
    systemctl set-property mysqld MemoryHigh=1500M

    5.2 查看内存使用

    # cgroup v2 内存文件
    cat /sys/fs/cgroup/system.slice/mysqld.service/memory.current # 当前使用
    cat /sys/fs/cgroup/system.slice/mysqld.service/memory.max # 限制
    cat /sys/fs/cgroup/system.slice/mysqld.service/memory.events # 事件计数器

    memory.events 字段:

    字段含义oomOOM 触发次数oom_kill实际杀掉进程的次数high超过 MemoryHigh 的次数max超过 MemoryMax 的次数
    # 如果 oom_kill 持续增长,说明 cgroup 限制太低
    cat /sys/fs/cgroup/system.slice/mysqld.service/memory.events
    # oom 12
    # oom_kill 3
    # ...

    5.3 告警脚本

    #!/bin/bash
    # /usr/local/bin/cgroup_mem_alert.sh
    # 检查所有 service 的 cgroup 内存使用率

    THRESHOLD=90 # 百分比

    for cg_dir in /sys/fs/cgroup/system.slice/*/; do
    service_name=$(basename "$cg_dir" .service)
    current=$(cat "$cg_dir/memory.current" 2>/dev/null)
    max=$(cat "$cg_dir/memory.max" 2>/dev/null)

    [ "$max" = "max" ] && continue # 未设置限制
    [ -z "$current" ] && continue
    usage=$((current * 100 / max))
    if [ "$usage" -gt "$THRESHOLD" ]; then
    echo "[ALERT] $service_name 内存使用 ${usage}% (${current}/${max})"
    fi
    done
    # 加入 cron,每 5 分钟检查
    echo "*/5 * * * * /usr/local/bin/cgroup_mem_alert.sh >> /var/log/cgroup_mem_alert.log" | crontab -

    六、三种语言的内存泄漏排查

    不同语言的泄漏特征不同,排查工具也不同。

    6.1 Ja va(JVM 堆泄漏)

    # 查看 JVM 堆使用
    jmap -heap

    # 导出堆 dump
    jmap -dump:format=b,file=/tmp/heap.hprof

    # 用 MAT (Memory Analyzer Tool) 分析
    # 或用 jhat(命令行)
    jhat -port 7000 /tmp/heap.hprof

    Ja va 泄漏常见模式:

    模式特征排查重点集合不清理HashMap 持续增长MAT 看 Dominator TreeThreadLocal 泄漏线程池线程不销毁看 ThreadLocalMap静态集合ClassLoader 泄漏看静态字段引用链连接未关闭ResultSet/Statement看 Finalizer 队列

    6.2 Python(引用计数+GC)

    # 启用 tracemalloc
    python3 -c "
    import tracemalloc
    tracemalloc.start()
    # ... 业务代码 ...

    snapshot = tracemalloc.take_snapshot()
    top_stats = snapshot.statistics('lineno')
    for stat in top_stats[:10]:
    print(stat)
    "

    Python 泄漏常见模式:

    模式特征排查重点循环引用GC 回收不了gc.get_objects()全局列表list 只 append 不 poptracemalloc 按行号闭包捕获变量被闭包持有objgraph.show_backrefsC 扩展泄漏引用计数管不到valgrind --tool=memcheck

    6.3 Go(GC 语言仍有泄漏)

    Go 有 GC 但仍会泄漏——goroutine 泄漏和 channel 阻塞是主要类型。

    # 查看 goroutine 数量
    curl http://localhost:6060/debug/pprof/goroutine?debug=1 | head -50

    # 查看 heap profile
    curl http://localhost:6060/debug/pprof/heap?debug=1 > heap.out
    go tool pprof heap.out
    # (pprof) top 10
    # (pprof) list

    Go 泄漏常见模式:

    模式特征排查重点goroutine 泄漏goroutine 数持续增长pprof goroutinechannel 阻塞发送/接收永远阻塞pprof goroutine stackslice 底层数组大 slice 切片后未释放看 slice 引用time.Ticker 未 Stop定时器泄漏pprof goroutine

    七、监控告警配置

    Prometheus 告警规则:

    groups:
    - name: memory
    rules:
    - alert: HostMemoryUsageHigh
    expr: (1 - node_memory_MemA vailable_bytes / node_memory_MemTotal_bytes) * 100 > 85
    for: 10m
    labels:
    severity: warning
    annotations:
    summary: "内存使用率超过 85%"

    - alert: HostSwapUsageHigh
    expr: (1 - node_memory_SwapFree_bytes / node_memory_SwapTotal_bytes) * 100 > 50
    for: 5m
    labels:
    severity: warning
    annotations:
    summary: "Swap 使用率超过 50%"

    - alert: ProcessOomKilled
    expr: increase(node_vmstat_oom_kill[5m]) > 0
    labels:
    severity: critical
    annotations:
    summary: "系统在 5 分钟内发生了 OOM Kill"

    八、常见误区

    误区一:MemFree 低就加内存

    MemFree 低但 MemA vailable 高,说明内存被缓存占了,这是正常行为。加内存解决不了问题,应该看业务是否真的需要那么多内存。

    误区二:OOM 杀进程就是内存泄漏

    单次 OOM 可能是突发流量、批量任务、配置错误(如 JVM 堆设太大)。只有 OOM 反复发生且进程重启后又快速被杀,才是泄漏。

    误区三:关闭 swap 提升性能

    关闭 swap 会让 OOM 更激进——内核没有 swap 作为缓冲区,一旦物理内存满就立即杀进程。线上服务建议保留 swap(哪怕很小),作为 OOM 的缓冲。

    误区四:cgroup 限制设得太紧
    cgroup MemoryMax 设得太紧会导致频繁 OOM Kill,设得太松失去隔离效果。建议初始设为业务峰值 P99 的 1.5 倍,观察一周后调整。

    九、排查清单

    发现内存问题,按这个顺序排查:

    1. dmesg -T | grep -i oom 确认是否 OOM,区分系统级/cgroup级
    2. /proc/meminfo 看系统级内存分布
    3. smem -rs pss 定位占内存最多的进程
    4. pmap -x 看进程内存映射,找 [anon] 增长
    5. slabtop 看内核 Slab 是否异常
    6. bpftrace 追踪分配热点
    7. 语言级工具(jmap/tracemalloc/pprof)定位代码
    8. cgroup v2 限制 + 告警配置
    9. Prometheus 告警长期监控

    写在最后

    内存问题排查,核心就两步:先把层级定位清楚,再选择合适的工具。系统级先看 /proc/meminfo,进程级看 smem/pmap,内核级看 slabtop,代码级就交给语言 profiler。cgroup v2 是线上服务做内存隔离的基础设施,每条服务都应该配上。至于 OOM Killer,它的行为其实是可预测、可保护的,千万别把它理解成随机事件。

    本文内容来源于网友投稿,如有侵权请联系删除。
    作者最新文章
    系统应用
    相关文章 更多
    docker容器自动重启怎么解决 排查原因与修复方法
    docker容器自动重启怎么解决 排查原因与修复方法

    遇到Docker容器频繁自动重启?本文提供从查看容器日志、解读退出代码到配置Restart Policy的完整排查与修复方案,确保服务稳定性。

    Linux设置静态路由命令与永久生效教程
    Linux设置静态路由命令与永久生效教程

    学习如何在Linux中使用命令行添加临时静态路由,并通过修改网络配置文件实现重启后依然有效的永久静态路由设置,涵盖CentOS和Ubuntu的不同配置方法。

    摄像头连接电脑后怎么打开查看实时画面教程
    摄像头连接电脑后怎么打开查看实时画面教程

    摄像头连接电脑后不知如何查看画面?本教程演示如何使用Windows自带的“相机”应用快速调取实时视频流,无需下载额外软件,步骤简单直观,适用于大多数USB及内置摄像头。

    如何在 windows 11 关闭自动关机
    如何在 windows 11 关闭自动关机

    针对 Windows 11 电脑在无人操作时自动关机或重启的问题,本文详解如何通过任务计划程序禁用更新重启任务,并调整电源睡眠策略,从根源解决非预期的自动关机现象。

    电脑摄像头怎么测试人脸识别功能及效果判断方法
    电脑摄像头怎么测试人脸识别功能及效果判断方法

    详细讲解在Windows电脑上测试摄像头人脸识别功能的步骤,包括开启Windows Hello、录入面部数据、不同环境下的效果验证及常见问题排查,帮助用户确认设备兼容性与识别准确率。

    docker 容器无法启动的配置检查命令及排查方法
    docker 容器无法启动的配置检查命令及排查方法

    当 Docker 容器启动后立即退出时,不要盲目重启。本文详解如何通过 docker logs 查看标准输出错误,利用 docker inspect 检查挂载与网络配置,并修正入口点脚本权限问题,提供一套高效的因果排查流程。

    安卓手机投屏到电脑win10怎么设置教程
    安卓手机投屏到电脑win10怎么设置教程

    详解如何利用Windows 10自带功能将安卓手机画面投射至电脑屏幕,涵盖开启步骤、连接技巧及延迟分析,适合临时演示与多屏协作场景。

    移动硬盘解除加密怎么操作?详细步骤教程
    移动硬盘解除加密怎么操作?详细步骤教程

    详细介绍如何在Windows系统中为移动硬盘解除BitLocker加密,包括解锁驱动器、关闭加密功能及监控解密进度的完整操作流程,帮助用户安全移除数据保护。

    笔记本电脑耳机没声音怎么设置?Windows 10解决方法
    笔记本电脑耳机没声音怎么设置?Windows 10解决方法

    本文详解Windows 10笔记本耳机无声音的常见原因与解决方法,涵盖物理接口检查、默认播放设备切换、驱动程序更新及音频增强关闭等操作,并分析蓝牙耳机连接异常的例外场景。

    sata固态硬盘安装win10系统怎么分区详细教程
    sata固态硬盘安装win10系统怎么分区详细教程

    还在为SATA固态硬盘装Win10怎么分区头疼?本教程详解安装过程中的分区步骤,涵盖4K对齐检查、ESP引导分区处理及容量规划建议,助你一次性搞定系统盘,避免后期卡顿与空间浪费。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    macOS软件
    macOS软件

    正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

    Mac软件 更多
    photoshop
    photoshop
    Windows、macOS 、 iPad

    Photoshop 2026 是 Adobe 推出的专业图像处理与视觉设计软件,支持 Windows、macOS 和 iPad 等平台,广泛应用于摄影修图、电商设计、平面海报、数字绘画及视觉合成等创作场景。

    Blender
    Blender
    Windows、macOS 和 Linux

    Blender 是一款免费开源、跨平台的专业 3D 创作软件,集建模、动画、渲染、视频编辑与视觉合成等功能于一体,广泛应用于影视动画、游戏设计和建筑可视化等领域。软件支持 Cycles 物理渲染器与 Eevee 实时渲染引擎,并提供多边形建模、骨骼绑定、物理模拟等专业工具。Blender 兼容 Windows、macOS 和 Linux 系统,安装包轻巧、运行流畅,依托活跃的全球开发者社区持续更新,是从初学者到专业创作者都值得选择的正版 3D 创作工具。

    灵活计算器
    灵活计算器
    macOS/iOS/Android

    灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

    WINDOWS 更多
    3dmax(3ds max)
    3dmax(3ds max)
    Windows

    Autodesk 3ds Max 是一款专业的三维建模、动画与渲染软件,广泛应用于建筑可视化、游戏开发、影视动画、广告设计和产品展示等领域。

    photoshop
    photoshop
    Windows、macOS 、 iPad

    Photoshop 2026 是 Adobe 推出的专业图像处理与视觉设计软件,支持 Windows、macOS 和 iPad 等平台,广泛应用于摄影修图、电商设计、平面海报、数字绘画及视觉合成等创作场景。

    Blender
    Blender
    Windows、macOS 和 Linux

    Blender 是一款免费开源、跨平台的专业 3D 创作软件,集建模、动画、渲染、视频编辑与视觉合成等功能于一体,广泛应用于影视动画、游戏设计和建筑可视化等领域。软件支持 Cycles 物理渲染器与 Eevee 实时渲染引擎,并提供多边形建模、骨骼绑定、物理模拟等专业工具。Blender 兼容 Windows、macOS 和 Linux 系统,安装包轻巧、运行流畅,依托活跃的全球开发者社区持续更新,是从初学者到专业创作者都值得选择的正版 3D 创作工具。