Slab,小对象也能搞出大事情

本文涉及的产品
云监控,每月短信1000条
简介:

排查内存使用(泄露、耗尽)问题的一个的技巧是,区分“批发商”和“零售商”这两类不同的内存管理机制。这里的“批发商”,指的是按页面管理并分配内存的机制。而“零售商”,则是指从“批发商”那里批量获取资源,并以字节为单位,管理和分配内存的机制。

“零售商”和“批发商”的区分很重要。这是因为通过“零售商”分配出去的内存资源,在“批发商”那里或多或少都有统计。但是从“批发商”那边分配出去的内存资源,“零售商”几乎一无所知。凡是一些诡异的,“我的内存去哪里了”的问题,往往都跟这个有点有关系。

“批发商”“零售商”的例子很多,比如Windows上的一对接口VirtualAlloc和HeapAlloc。而在Linux内核中,buddy system毋庸置疑是最大内存的“批发商”,而slab则是最常用的“零售商”。今天这篇文章,我跟大家分享一个和“零售商”slab有关的真实案例。

云监控是一个好产品

今天的案例,我们从云监控说起。云监控作为一个监控工具,给客户提供了非常丰富的功能。阿里云的客户可以使用云监控来随时了解云服务的状态,而且在云服务出现异常的时候,客户可以及时地收到来自云监控的报警信息。

做为阿里云技术支持的同学,可以说我们对云监控有着一种非常“特殊”的感情。这是因为,很多时候客户提给我们的问题,会以一个云监控的截图开始,看到云监控的截图,那基本就等于我们有新的问题需要处理了。

以上的截图是云监控通过钉钉消息发送给客户的报警信息。从这个截图里来看,客户的一台ECS实例内存使用率超过了90%,而且持续了1分钟时间。这对ECS服务器来说,是相当不健康的状态。

基本套路

处理内存使用问题,有一些基本套路。首先,我们可以使用free或top命令看一下系统总体内存使用情况,如物理内存大小,有多少剩余内存,有多少被cache(这里的cache和slab机制的cache是两回事)和buff用掉了;其次,我们可以使用ps命令看看每个进程内存的使用情况;如果到这一步还没有定位到问题,进一步,我们可以通过查看/proc/meminfo文件,在更细的粒度上了解内存资源的分布与分配情况。按照这个套路打下来,一般我们都会得到一个初步的排查结果。以今天这个问题为例,系统32G内存几乎被用光了,但是cache和buff并不大,进程使用的内存也不多。最终在meminfo文件中,我发现有大量内存被slab用掉了。

零售商slab

Slab应该算是Linux内存管理机制中,最著名的“零售商”机制。写slab的文章非常多,我这里只做简单的介绍。

做为“零售商”,slab肯定需要解决两个问题,一个是怎么样从“批发商”buddy system那里“批发”内存,另外一个是怎么样管理并把这些内存“散卖”出去。为了回答这两个问题,我们首先理解一下slab机制的数据结构。Slab机制包括三个层次的数据结构:cache,slab和object。如下图,一个slab一般是一个4K大小的内存页面。当我们把slab页面,按照固定大小切分成能够被“零售”的小块的时候,我们就得到了很多的object。当把包含相同大小object的slab用“箭头”串起来的时候,就形成了cache。这里slab和object是比较容易理解的:因为把4K的页面分配给几个字节的对象使用太浪费内存了,所以就需要切开来“散卖”。而cache的存在意义其实是比较隐晦的。Cache存在的意义,其实是为了动态的管理slab,让slab可以动态的增加和减少。在object需求量大的时候,可以从“批发商”那里拿到很多的slab放进cache里,而在用完之后,又可以动态地把slab退回去。

基于cache, slab和object数据结构,slab机制要解决上边那两个问题就变得很容易了:一方面,slab机制从“批发商”buddy system那里,以slab(页面)为单位获取内存;另外一方面,slab机制以object为基本管理单元,把内存资源“零售”给系统中其他模块。

Slabtop是一个可以用来查看slab内存使用情况的工具。下边这个截图来自客户问题现场。我们可以根据cache size这一列,快速地定位到问题。很明显cache size最大的一行对应的对象是dentry。

注意:为了解释简单,这里我假设slab是一个页面,也就是4K大小。这个假设意味着,所有被管理的对象都是小于4K的。但在实际情况中,为了提供大于4K的内存块,一个slab其实可以是多个连续的物理页面。

小对象搞出大事情

目录项dentry算是一个非常小的内核数据结构。从上边的截图我们可以看到,dentry其中只有0.19K。虽然dentry很小,但是这个结构是内核中最常使用的数据结构。如下图,当进程打开文件的时候,一般都会有对应的dentry被分配出来。在Linux这样的“一切皆文件”的操作系统里,dentry结构被使用频率是不言自明的。

因为已经定位到了内核数据结构dentry,所以我选择抓一个dump来慢慢分析这个问题。抓了dump之后让客户重启机器释放内存,以免导致更严重的后果,影响业务。

Core dump掘金

使用core dump排查问题有两个优势:一是在生产环境中,客户有时候真的很难给我们机会去使用一些trace和工具一步一步的排查问题,而抓dump对业务的影响较小,抓完之后可以慢慢分析。二是core dump里包含了系统中所有正常、不正常的状态。数据量大且完整,这不是一般的数据收集方法可以比的。下边我演示一遍怎么用core dump来排查slab泄露问题。

系统基本信息

使用sys命令,我们可以快速看一下客户这台机器的基本信息。如机器名,内核版本,内存大小等。

内存概要

因为这个问题是内存使用问题,我们可以用kmem -i命令在dump里看一下内存的使用情况。我们可以看到,这个系统有32G内存,其中slab用掉27G。

Cache

接下来我们看一下slab的使用情况。命令kmem -s可以帮我们列出内核中所有的slab cache。如下图,每一行对应一个cache,每个cache负责管理一个对象,第二列是对象的名字。

Slab

既然已经发现了问题是由dentry对象引起的,那么,我们可以到slab结构内部去查看泄露的的dentry。命令kmem -S dentry可以帮我们列出包含dentry的所有slab。可以对照“零售商slab”一节第一个插图来看下边的截图。前两行给出了cache的一些基本信息,如地址,object名字,object大小等。从第三行开始,这个命令分块输出cache中所有的slab结构。每个slab里包含一列小对象,这些对象都在一个页面内部,从他们连续的地址可以看出这点。实际上这个系统分配了7百万slab页面给dentry,下图只截了前两个。

小对象dentry

这个时候问题来了。这个cache里有7百万slab,有1亿4千万dentry项,我们怎么去分析这些项呢?其实有一个很简单的技巧,就是随机挑选的一些dentry,然后看看这些dentry项里,有没有什么共同的特征,比如相同的字符串,或者共同引用的地址等。对这个问题来说,我是很幸运的。我发现在随机挑选的dentry结构里,d_iname这个字符串都包含一个子串“dOeSnotExist_.db”。

使用dOeSnotExist_.db这个子串,我们可以在公网上轻而易举地定位到下边这个问题:这是nss-softokn软件包的一个Bug。

后记

技术支持工作的一个创新点,也是这个工种的一大乐趣,是可以利用各种组合技,利用不同的方法,巧妙地组合并找出问题的答案。以今天这个问题为例,我们开始使用了一些基础的排查工具对问题进行了初步的定位;进而我们使用了core dump这种看起来不那么易用的工具,从内存中挖出了一个字符串作为线索;最后结合公网上的信息,我们拼出了这个问题的答案。

相关实践学习
基于云监控实现的监控系统
通过阿里云云监控功能给非阿里云主机安装监控插件,从而实现对非阿里云主机的各项指标进行监控和管理,在配置报警规则和报警人的情况下,能对特定的场景做出报警反应通知到报警人的手机上。
目录
相关文章
|
6月前
|
存储 程序员 编译器
|
5月前
|
存储 机器学习/深度学习 Linux
程序员必知:关于Linux内存寻址与页表处理的一些细节
程序员必知:关于Linux内存寻址与页表处理的一些细节
46 0
|
6月前
|
Java API
DirectByteBuffer内存释放原理
DirectByteBuffer内存释放原理
66 0
|
6月前
|
存储 算法 Linux
深入理解Linux内存管理brk 和 sbrk 与以及使用C++ list实现内存分配器
深入理解Linux内存管理brk 和 sbrk 与以及使用C++ list实现内存分配器
602 0
|
6月前
|
缓存 算法 安全
Linux内存管理宏观篇(五)物理内存:页面分配和释放页面
Linux内存管理宏观篇(五)物理内存:页面分配和释放页面
360 1
系统明明有很多内存,却无法分配出一片大块内存?
系统明明有很多内存,却无法分配出一片大块内存?
|
Java
40-对象太多了!堆内存实在是放不下,只能内存溢出!
之前通过三篇文章的分析,介绍了 直接内存、Metaspace和栈内存三块区域的内存溢出,同时给出了一些常见的引发内存溢出的场景以及对应解决方案,一般只要vm参数配置合理,代码上不出现大问题,一般不太容易引发对应的OOM
187 0
40-对象太多了!堆内存实在是放不下,只能内存溢出!
|
存储 程序员 调度
一看就会的动态内存管理,确定不来看一看?教你快速学会内存管理
一看就会的动态内存管理,确定不来看一看?教你快速学会内存管理
68 0
|
存储 算法 Java
JVM 系列(4)吊打面试官:对象的内存分为哪几个部分?
JVM 系列(4)吊打面试官:对象的内存分为哪几个部分?
134 0
JVM 系列(4)吊打面试官:对象的内存分为哪几个部分?
|
存储 C++
一顿饭的功夫带你了解block是如何内存管理的
一顿饭的功夫带你了解block是如何内存管理的
155 0
一顿饭的功夫带你了解block是如何内存管理的