Linux内核内存泄漏排查:从free告警到meminfo与slabinfo实战

发布时间:2026/10/17 12:45:24

Linux内核内存泄漏排查:从free告警到meminfo与slabinfo实战 1. 从一次线上告警说起为什么free内存充足却频繁OOM凌晨两点被电话叫醒监控面板上free -m显示还有3个G的可用内存但业务进程已经被内核OOM Killer干掉了。这种内存明明够用却报内存不足的场景做过几年运维或后端开发的人大概率都遇到过。第一反应通常是去看应用日志、查堆内存、翻GC记录折腾一圈发现应用层看起来干干净净问题却依然存在。这时候真正该盯的不是应用层而是内核层。Linux的内存管理是一个多层账本系统free命令看到的只是最外层的一个快照它把大量内核内部消耗的内存都算进了已用里但不会告诉你这些内存到底被谁拿走了。/proc/meminfo和/proc/slabinfo这两个文件才是深入内核内存账本的两把钥匙。这篇内容适合谁看一是遇到内存缓慢增长、重启能缓解但找不到根因的运维和SRE二是写内核模块或驱动、怀疑自己代码有泄漏的开发者三是对Linux内存机制感兴趣、想把free、meminfo、slab这几层关系彻底搞明白的技术人。我会从实际排查链路出发把meminfo的字段含义、slab的统计逻辑、以及如何用它们交叉定位泄漏点讲透中间穿插我自己踩过的坑和验证方法。需要先明确一个前提内存泄漏在Linux语境下分两种一种是用户态进程的堆泄漏比如malloc了没free另一种是内核态的泄漏比如kmalloc了没kfree、slab对象只增不减。前者用valgrind、heaptrack这类工具能搞定后者才是meminfo和slab的主战场。本文聚焦后者因为这类问题最隐蔽、最难查也最容易在半夜把你叫醒。2. 读懂/proc/meminfo别只盯着MemFree那一行2.1 MemFree、MemAvailable、Buffers、Cached到底谁说了算很多人看内存只看MemFree这是最大的误区。MemFree表示的是完全未被使用的物理内存但Linux的设计哲学是空闲内存就是浪费所以它会尽可能把内存拿去做页缓存page cache。你看到MemFree很小不代表内存紧张。真正该看的是MemAvailable。这个字段是内核估算出来的在不触发swap的前提下还能给新应用分配多少内存它把大部分可回收的页缓存和slab可回收部分都算进去了。判断内存是否真的吃紧MemAvailable比MemFree靠谱得多。# 一个典型的meminfo片段 MemTotal: 16384000 kB MemFree: 204800 kB MemAvailable: 8192000 kB Buffers: 102400 kB Cached: 6144000 kB Slab: 2048000 kB SReclaimable: 1536000 kB SUnreclaim: 512000 kB这里有个关键点Slab SReclaimable SUnreclaim。SReclaimable是内核可以回收的slab内存比如dentry缓存、inode缓存内存紧张时会被释放SUnreclaim是内核无法回收的slab内存这部分才是真正钉死在内存里的。如果你的SUnreclaim持续增长且不回落基本可以锁定内核slab泄漏。2.2 用watch盯住关键字段的变化趋势单次快照说明不了问题泄漏的本质是趋势。我习惯用下面这条命令持续观察watch -n 5 grep -E MemAvailable|Slab|SReclaimable|SUnreclaim|KernelStack|PageTables /proc/meminfo重点盯三个方向SUnreclaim是否单调递增、KernelStack是否异常膨胀线程泄漏的典型信号、PageTables是否持续走高大量mmap或内存映射泄漏。如果SUnreclaim在业务低峰期也不下降那基本可以排除是正常缓存行为往泄漏方向查。提示观察周期至少覆盖一个完整的业务波峰波谷短时间内的波动可能只是缓存预热别急着下结论。2.3 那些容易被忽略的隐形内存项除了Slabmeminfo里还有几个字段经常被忽视但往往是泄漏的真凶KernelStack每个内核线程占用的栈空间如果线程只创建不回收这个值会线性增长。PageTables页表本身占用的内存大量小内存映射或频繁mmap/munmap会导致它膨胀。VmallocUsedvmalloc区域的使用量某些驱动用vmalloc分配大块内存泄漏时这里会很明显。Percpu每CPU变量占用的内存和CPU数量相关一般稳定异常增长说明有per-cpu分配泄漏。我遇到过一次案例SUnreclaim平稳但KernelStack每小时涨几十KB最后定位到是某个内核模块反复创建workqueue线程却没销毁。所以排查时不要只盯Slab把这几项一起纳入观察范围。3. 解剖/proc/slabinfo找到那个只增不减的缓存3.1 slabinfo的列到底在说什么/proc/slabinfo的原始输出对新手不太友好建议先看/proc/slabinfo的格式化版本或者用slabtop。先理解列含义# 原始格式前几列 # name active_objs num_objs objsize objperslab pagesperslab kmalloc-64 10240 10240 64 64 1 dentry 512000 520000 192 21 1关键列解读列名含义排查价值active_objs当前活跃对象数持续增长说明对象被持有未释放num_objs总分配对象数与active_objs的差值反映空闲对象objsize单个对象大小结合对象数估算总内存占用objperslab每个slab页容纳的对象数用于计算slab页数量计算某个缓存占用的总内存num_objs * objsize / 1024得到KB数。比如dentry有520000个对象每个192字节就是约95MB。3.2 用slabtop快速排序定位大户slabtop是排查slab问题的第一把利器它能按内存占用或对象数排序几秒钟就能看出谁是大户# 按内存占用排序每2秒刷新一次 slabtop -s c -d 2 # 按对象数排序 slabtop -s n -d 2-s c按缓存大小排序-s n按对象数量排序。我一般先按大小排找到占用最高的几个缓存然后观察它们的ACTIVE和NUM是否在增长。如果某个缓存的NUM持续上涨而ACTIVE不降那就是泄漏嫌疑对象。3.3 区分正常增长和真泄漏不是所有增长的slab都是泄漏。dentry和inode缓存会随着文件访问自然增长这是正常的而且它们属于SReclaimable内存紧张时会被回收。真正要警惕的是SUnreclaim里的缓存以及那些名字看起来就很业务相关的自定义缓存。判断方法在业务低峰期比如凌晨没有请求时观察。如果缓存能回落说明是正常缓存如果纹丝不动甚至继续涨那就是泄漏。我通常会写个小脚本每隔10分钟记录一次目标缓存的num_objs跑一晚上看趋势曲线。# 记录kmalloc-256的对象数变化 while true; do date /tmp/slab_trace.log grep kmalloc-256 /proc/slabinfo /tmp/slab_trace.log sleep 600 done4. 把meminfo和slabinfo串起来一条完整的定位链路4.1 第一步确认泄漏发生在内核态还是用户态先看SUnreclaim的趋势。如果它持续增长基本锁定内核态slab泄漏直接进slabinfo排查。如果SUnreclaim平稳但MemAvailable持续下降那要往用户态进程查用ps aux --sort-rss找RSS最高的进程再结合/proc/pid/smaps分析。还有一种情况SUnreclaim和用户态RSS都正常但KernelStack或PageTables异常那就往线程泄漏或内存映射泄漏方向查。4.2 第二步在slabinfo里锁定嫌疑缓存确认是slab泄漏后用slabtop -s c找到占用最高的几个缓存然后对每个嫌疑缓存做趋势观察。重点关注两类通用缓存kmalloc-*系列。如果kmalloc-256这类通用缓存持续增长说明有代码在用kmalloc分配但没释放但具体是谁分配的不好定位。专用缓存名字带业务前缀的比如my_driver_ctx、foo_cache。这类缓存是kmem_cache_create创建的一旦泄漏名字直接指向模块定位效率极高。提示如果怀疑是某个内核模块可以先lsmod看加载了哪些模块再对照slabinfo里的缓存名很多模块会用模块名做缓存前缀。4.3 第三步用tracepoint抓分配调用栈锁定嫌疑缓存后光知道谁在涨还不够得知道谁在分配。这时候要用到内核的slab tracepoint。以kmalloc为例# 开启kmalloc的tracepoint echo 1 /sys/kernel/debug/tracing/events/kmem/kmalloc/enable # 抓一段时间后关闭 echo 0 /sys/kernel/debug/tracing/events/kmem/kmalloc/enable # 查看结果 cat /sys/kernel/debug/tracing/trace | head -100trace里会记录每次分配的调用栈如果某个调用栈出现频率极高且对应的是只分配不释放的路径那就是泄漏点。这个方法需要内核开启了CONFIG_TRACEPOINTS和debugfs生产环境不一定有但测试环境复现时非常有效。4.4 第四步交叉验证排除误判定位到嫌疑点后别急着改代码先做交叉验证。方法包括卸载可疑模块看SUnreclaim是否停止增长、用kmemleak做一次全量扫描、或者在测试环境构造相同负载复现。kmemleak是内核自带的泄漏检测工具需要内核编译时开启CONFIG_DEBUG_KMEMLEAK。开启后它会周期性扫描内存把疑似泄漏的地址和调用栈记录在/sys/kernel/debug/kmemleak里。虽然有一定误报率但作为交叉验证手段非常有用。# 触发一次扫描 echo scan /sys/kernel/debug/kmemleak # 查看疑似泄漏 cat /sys/kernel/debug/kmemleak5. 几个真实场景的排查复盘5.1 场景一kmalloc-256缓慢增长最后定位到网络收包路径某次线上服务运行一周后SUnreclaim涨了800MBslabtop显示kmalloc-256是最大户。用tracepoint抓了一分钟发现大量分配来自网络驱动的收包路径。进一步查代码发现驱动在处理某种异常包时分配了skb但错误路径没释放。修复后SUnreclaim稳定在200MB左右。这个案例的教训是通用缓存泄漏最难定位因为kmalloc-256被无数代码路径共用必须靠tracepoint抓调用栈才能缩小范围。5.2 场景二自定义缓存只增不减模块卸载后内存不回收另一个案例是某内核模块创建了自定义slab缓存但模块卸载时没有销毁缓存导致内存永久占用。slabinfo里能看到这个缓存名但模块已经rmmod了缓存还在。这种情况只能重启或者写个脚本手动清理不推荐风险高。正确的做法是在模块的exit函数里调用kmem_cache_destroy并且确保所有对象都已释放。我后来在代码review清单里加了一条任何kmem_cache_create必须配对kmem_cache_destroy。5.3 场景三dentry缓存暴涨其实是正常的有次看到dentry缓存占了2GB吓了一跳结果发现是业务在遍历大量小文件dentry缓存自然增长。因为dentry属于SReclaimable手动echo 2 /proc/sys/vm/drop_caches就能释放。所以看到缓存大不要慌先确认它在SReclaimable还是SUnreclaim里。6. 排查工具与参数速查6.1 常用命令清单命令用途watch -n 5 grep ... /proc/meminfo持续观察内存趋势slabtop -s c -d 2按大小排序查看slabslabtop -s n -d 2按对象数排序查看slabcat /proc/slabinfo原始slab数据echo scan /sys/kernel/debug/kmemleak触发kmemleak扫描echo 2 /proc/sys/vm/drop_caches释放可回收缓存6.2 关键内核配置项排查slab泄漏前确认内核开启了这些配置CONFIG_SLUB_DEBUGslub调试、CONFIG_DEBUG_KMEMLEAKkmemleak、CONFIG_TRACEPOINTStracepoint、CONFIG_DEBUG_FSdebugfs。生产内核为了性能通常不开这些所以线上定位靠趋势观察测试环境复现靠这些工具。6.3 我个人的排查习惯我一般会先花10分钟做趋势观察确认是内核态还是用户态问题再决定用哪套工具。最忌讳一上来就上重型工具既慢又可能干扰生产。趋势观察阶段用watch加slabtop就够了锁定嫌疑对象后再上tracepoint或kmemleak。另外记录很重要。我会把每次观察的时间、SUnreclaim值、top缓存名记在一个表格里跑几个小时就能看出规律。有次就是靠这个表格发现泄漏和某个定时任务强相关直接缩小了排查范围。最后分享一个小技巧如果怀疑是某个模块可以在测试环境用rmmod卸载它然后观察SUnreclaim是否停止增长。这个方法简单粗暴但非常有效能快速排除或确认模块嫌疑。当然生产环境慎用卸载正在使用的模块可能导致业务中断。
延伸阅读

更多相关文章

2026/10/17 12:45:24

WSL2下安装配置Claude Code实战:从零到稳定使用的完整指南

先说结论:如果你手头有一台Windows电脑,日常又要在Linux环境里写代码、跑脚本、对接服务器,那在WSL2里装Claude Code基本是当前体验最好的组合之一。这篇文章不是官网文档的复读,是我把WSL2环境从零搭到Claude Code能日常稳定使用…

2026/10/17 12:45:24

Git仓库批量镜像下载器:备份、迁移与增量同步实践

简介:围绕“repository下载”场景整理的离线依赖资源包,主要面向Java开发者、Maven使用者,以及需要搭建本地或内网依赖仓库的团队。压缩包为rar格式,共收录2000个文件,包体约324MB,核心内容以jar依赖包、po…

2026/10/17 12:40:24

PyCharm 接入 DeepSeek:用 Continue 插件实现 AI 代码补全与对话调试

1. 为什么要在 PyCharm 里接入 DeepSeek1.1 从“写代码”到“聊代码”的转变我用了七八年 PyCharm,从最早的社区版一路用到专业版,说实话,真正让我觉得“回不去”的,不是某个重构功能,而是把大模型接进 IDE 之后的那种…

2026/10/17 13:55:29

数学建模竞赛备赛全攻略:从模型库到论文交付的完整知识地图

简介:面向全国大学生数学建模竞赛选手与培训者的系统性学习包,内容涵盖数模培训讲义、历年赛题解读、优秀获奖论文及可直接运行的MATLAB源代码。资料从概率统计、优化方法等基础理论入手,结合蒙特卡洛模拟、神经网络、微分方程建模等常见方法…

2026/10/17 13:55:29

LeetCode 61 旋转链表:思路演进、代码实现与高频踩坑点

在很多算法训练手册里,链表题往往被归为“入门容易,精通难”的那一类。链表本身不复杂,无非是节点结构和指针操作,但一旦涉及边界条件、指针移动步数、循环与取模,许多人就开始头大。LeetCode 61题“旋转链表”恰好就是…

2026/10/17 13:55:29

基于Scrapy与Django的二手房数据采集、可视化与房价预测系统

1. 项目拆解与整体方案设计 1.1 我为什么选择做这个项目 这两年数据分析岗位的竞争越来越激烈,简历上光写“我会Python”“我熟悉SQL”已经没什么说服力了。我当时的想法是:要做就做一个 有真实数据来源、有完整业务闭环、能展示工程能力 的项目。房地…

2026/10/17 13:55:29

Kafka Producer Sender线程深度解析:核心机制与调优实战

1. Kafka Sender线程的核心定位与设计哲学1.1 为什么Sender线程是Kafka Producer的“心脏”很多刚接触Kafka Producer的同学,写代码时就是调一个producer.send(record),然后要么不管,要么调一下get()同步等结果。代码能跑通,但一旦…

2026/10/17 13:50:29

虚拟存储器课后题精讲:地址转换、缺页处理与页面置换算法实战

1. 虚拟存储器到底在解决什么问题1.1 从一道课后题说起:为什么需要虚拟存储器2022版操作系统教材里关于虚拟存储器的课后题,我印象最深的是那道“某进程访问逻辑地址0x0000,页表项中有效位为0,会发生什么”的题目。很多同学第一反…

2026/10/16 5:33:43

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/16 4:03:48

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/15 13:42:16

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/17 0:04:32

PlanarSLAM:面向曼哈顿世界的点线面联合SLAM系统

简介:本资源是面向机器人视觉与SLAM研究者的C开源RGB-D SLAM系统实现,专为结构化室内场景(如办公室、走廊)设计,通过融合点、线、平面三类几何特征并引入“曼哈顿世界”假设,显著提升位姿跟踪鲁棒性与稠密地…

2026/10/17 0:04:32

TDH Workflow调度实战:从DAG依赖驱动到任务编排与故障排查

1. 从一个调度脚本说起:TDH Workflow到底在解决什么问题第一次接触TDH的Workflow,是在一个数据清洗项目里。当时团队每天要跑几十个ETL任务,任务之间有严格的先后依赖——A表清洗完才能跑B表的关联,B表跑完才能触发C表的聚合。最开…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑