导读:本期聚焦于冷风创作的《Linux下slab内存泄漏如何排查?一份实用的问题定位指南》,敬请观看详情。服务器free命令显示available内存持续下降,但进程RSS加起来远没那么多,多余的内存到底去哪了?这类问题十有八九和内核slab缓存有关。slab是Linux内核为频繁分配释放的小对象设计的内存管理机制,dentry、inode、各种文件描述符缓存都会挂在这里,一旦某个缓存只增不减,就会出现典型的内存泄漏现象。本文从slab的底层原理讲起,介绍dentry、kmalloc等常见缓存类型,再通过free、/proc/meminfo、slabtop等工具逐步缩小范围,最后结合kmemleak和bpftrace定位到泄漏源头,帮你把排查思路和命令一次梳理清楚。

排查内存泄漏时,很多人习惯只盯着进程的RSS看,但有一种泄漏发生在内核侧,进程内存看起来一切正常,机器的可用内存却持续下滑。这类问题大多出在slab缓存上。slab是Linux内核用于管理小对象内存分配的机制,文件系统的dentry缓存、inode缓存、网络协议栈的各种结构体,都由slab统一管理。一旦某个slab缓存的对象只分配不释放,就会表现为典型的内核内存泄漏,严重时触发OOM killer,把无辜进程杀掉。这篇文章从原理到实操,完整梳理一遍slab内存泄漏的排查思路。

Linux下slab内存泄漏如何排查?一份实用的问题定位指南

先搞清楚slab是什么,泄漏发生在哪一层

Linux内核把物理内存按用途分成几个大块:用户态进程占用的匿名页、文件页缓存,以及内核自身使用的内存。内核内存中又有一大块交给slab分配器管理。slab分配器的思路很简单:内核里有大量固定大小的小对象需要频繁创建和销毁,比如每个打开的文件对应一个struct file,每个目录项对应一个dentry,如果每次都走伙伴系统按页分配,开销太大还容易产生碎片。于是内核预先按对象大小建好一批缓存(kmem_cache),每个缓存内部由若干slab页组成,分配时直接从空闲对象链表上摘一个,释放时挂回去,速度极快。

可以这么理解分层关系:伙伴系统管理4KB及以上的页,slab在页的基础上切分小对象,kmalloc系列函数则是对通用大小slab缓存的封装。所以slab泄漏的本质,是某个kmem_cache中active对象数量持续增长,说明对应的功能模块在不停创建对象却没有释放。

常见的泄漏大户有这几类:dentry缓存(文件路径频繁访问但不释放,比如某些程序会不停stat不存在的路径)、inode缓存(大量创建临时文件又异常退出没删干净)、kmalloc-XX通用缓存(驱动或内核模块自己kmalloc后忘了kfree)、以及网络相关的skbuff_head_cache(套接字缓冲区泄漏)。知道这些分类,后面看slabtop时心里就有谱了。

第一步:确认泄漏确实是slab引起的

排查从free命令开始。重点不是看free列,而是看buff/cache和available。如果available持续下降,而所有进程的RSS总和远小于已用内存,基本可以判断内存被内核吃掉了。再用下面这条命令确认:

cat /proc/meminfo | grep -i slab
# 输出示例:
# Slab:          12456984 kB
# SReclaimable:   9800452 kB   # 可回收部分,主要来自dentry/inode等
# SUnreclaim:     2656532 kB   # 不可回收部分,泄漏常发生在这里

SReclaimable是可回收的slab,内存紧张时内核可以主动释放,一般不用担心。真正要警惕的是SUnreclaim持续增长且降不下来。判断方法很简单:观察SUnreclaim的数值变化趋势,可以写个循环定时记录:

while true; do
  echo "$(date +%T) SUnreclaim: $(grep SUnreclaim /proc/meminfo)" >> /var/log/slab_watch.log
  sleep 60
done

如果SUnreclaim每小时稳定上涨且从不回落,泄漏基本坐实。另一个佐证手段是对比运行中的内核模块:如果机器上有自研驱动或第三方内核模块,先把可疑模块卸载,观察SUnreclaim是否停止增长,能快速把范围缩小到具体模块。

第二步:用slabtop找出泄漏的缓存对象

确认是slab泄漏后,下一步是找出哪个kmem_cache在膨胀。slabtop是最直观的工具,它实时展示各缓存的内存占用排行:

slabtop -o -s c | head -20
# -o 不进入交互模式,-s c 按缓存大小排序
# 重点关注 OBJ ACTIVE列(活跃对象数)和 SIZE列

找到异常缓存后,还需要确认它对应的内核功能。kmalloc-8k、kmalloc-1k这类通用缓存没有符号信息,可以配合/proc/slabinfo观察对象数量增长节奏,再结合业务行为推断。例如每秒处理一个请求,kmalloc-512的对象数也每秒加一,那泄漏点大概率在请求处理路径上。

对于dentry和inode缓存,可以直接查看总量并尝试手动回收验证:

cat /proc/sys/fs/dentry-state
# 输出: nr_dentry nr_unused ...
# 第一个数字是dentry总数,泄漏时往往达到千万级

# 手动回收dentry和inode缓存(生产环境注意,会有短暂性能抖动)
echo 2 > /proc/sys/vm/drop_caches

执行drop_caches后如果内存被大量释放,说明只是缓存堆积,还没到泄漏的程度,可以通过调低vm.vfs_cache_pressure来缓解;如果回收不了多少,说明这些dentry都被引用着,属于真泄漏,需要继续往深处查。一个很典型的案例:某个监控agent每隔几毫秒就去stat一批不存在的路径,每个失败路径的dentry都会缓存negative entry,日积月累就把Slab撑爆了,这种情况用strace跟踪一下系统调用就能发现。

第三步:用kmemleak或bpftrace定位到具体代码

缩小到某个缓存后,最后一公里是找到是谁在分配不释放。如果是自己能改内核配置的环境,kmemleak是最强的武器。它类似内核版的垃圾回收器,开启后内核会记录所有kmalloc的分配栈,定期扫描内存,找不到引用指针的分配就视为疑似泄漏:

# 内核需开启 CONFIG_DEBUG_KMEMLEAK
# 启动扫描
echo scan > /sys/kernel/debug/kmemleak
# 查看泄漏报告,包含完整内核调用栈
cat /sys/kernel/debug/kmemleak

报告中的调用栈会直接告诉你泄漏发生在哪个函数,比如看到ext4_xattr_block_cache相关的栈,就知道是扩展属性处理路径的问题。kmemleak的代价是约10%的性能损耗,且需要重新编译内核或发行版预置支持,适合在测试环境复现问题时使用。

生产环境不方便开kmemleak时,可以用bpftrace做低开销追踪。它能把探针挂在kmem_cache_alloc上,按调用栈聚合统计,几行脚本就能找出分配大户:

#!/usr/bin/env bpftrace
# 统计kmalloc的调用栈,按栈聚合出现次数
kprobe:kmem_cache_alloc
{
  @stack[kstack] = count();
}
interval:s:30 { print(@stack); clear(@stack); exit(); }

跑三十秒,输出中出现次数异常多的调用栈就是重点怀疑对象。再对比kmem_cache_free的统计,某个栈只分配不释放,泄漏点就锁定了。拿到栈信息后,回到对应的内核子系统或驱动代码,检查错误路径上是否漏掉了释放逻辑——绝大多数内核内存泄漏都藏在异常分支里,正常路径往往是对的。

排查思路小结

整个过程可以归纳成一条递进的线索:先用free和/proc/meminfo确认是内核内存问题,区分SReclaimable和SUnreclaim;再用slabtop和/proc/slabinfo定位膨胀的缓存对象,结合drop_caches验证是真泄漏还是缓存堆积;最后用kmemleak或bpftrace拿到分配调用栈,回到源码修复。平时建议对线上机器配置SUnreclaim的监控告警,超过经验阈值就提前介入,比等到OOM再排查从容得多。另外养成一个习惯:排查slab泄漏时同步记录当时的业务流量和内核模块版本,很多泄漏只在特定内核版本或特定负载形态下触发,这些信息对后续定位至关重要。

slab内存泄漏Linux内存排查slabinfo修改时间:2026-09-07 03:38:57

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260907/51949.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。