已归录

故障现象

一台应用主机接收到可用内存比例过低的告警!

故障分析

登录后查看:

# free -m
             total       used       free     shared    buffers     cached
Mem:         15953      15588        364          2        314       1515
-/+ buffers/cache:      13757       2195
Swap:        32767         26      32741

PS:free 表示完全没有使用的内存;shared 表示进程间共享的内存;buffers 表示缓存文件的元数据;cached 表示缓存文件的具体内容。

我们粗略估算一下可用内存:(364+314+1515)/15953 约等于 13%。

那么,到底是谁使用了这么多内存呢?我们使用 top 命令按内存使用率排序:

top - 21:08:46 up 729 days,  2:46,  1 user,  load average: 0.00, 0.00, 0.00
Tasks: 217 total,   1 running, 216 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.2%us,  0.3%sy,  0.0%ni, 99.5%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:  16335932k total, 15962972k used,   372960k free,   322524k buffers
Swap: 33554428k total,    27276k used, 33527152k free,  1552292k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                                                                       
 3554 gdm       20   0  375m  33m 6696 S  0.0  0.2   1114:46 gnome-settings-                                                                                                
 2944 root      20   0 7362m  29m 1432 S  0.0  0.2 120:06.07 virt-who                                                                                                       
 3430 root      20   0  229m  26m 4856 S  0.0  0.2  51:46.62 Xorg                                                                                                           
 4952 daemon    20   0 2073m  15m 1780 S  0.0  0.1 432:06.61 httpd                                                                                                          
 8389 daemon    20   0 2073m  15m 1780 S  0.0  0.1 415:41.82 httpd                                                                                                          
12648 daemon    20   0 2073m  15m 1884 S  0.0  0.1 375:24.27 httpd                                                                                                          
 4950 daemon    20   0 2073m  14m 1776 S  0.3  0.1 421:24.36 httpd                                                                                                          
 3786 daemon    20   0 2073m  14m 1776 S  0.0  0.1 279:46.04 httpd 

....
 

可以看到并没有什么进程有大量的内存消耗。

我们再统计一下所有进程的内存使用总量:

# ps aux | awk '{sum+=$6} END {print sum/1024}'
361.559

可见,所有进程使用的内存只有 360M 左右,那其它内存哪儿去了呢?

既然应用层没有大量的内存使用,那么就应该排查内核空间的使用情况了,最常见的就是观察 slab 了:

# cat /proc/meminfo 
......
Slab:           13644596 kB
SReclaimable:   13603636 kB
SUnreclaim:        40960 kB
......

可见,Slab 消耗掉了 13G 的内存空间,且基本上全部是可回收的部分。

Slab 机制专门用于缓存内核的数据对象,主要是用来缓存 Linux 内核中的小对象,比如 inode,dentry,可以提高性能并减少内存碎片。

那到低是什么对象使用了这么多 Slab 空间呢?我们可以使用 slabtop 命令查看或者查看 /proc/slabinfo:

# slabtop --sort=o
 Active / Total Objects (% used)    : 67450100 / 67692297 (99.6%)
 Active / Total Slabs (% used)      : 3403998 / 3404005 (100.0%)
 Active / Total Caches (% used)     : 104 / 182 (57.1%)
 Active / Total Size (% used)       : 12736934.70K / 12778744.17K (99.7%)
 Minimum / Average / Maximum Object : 0.02K / 0.19K / 4096.00K

OBJS         ACTIVE      USE     OBJ SIZE      SLABS         OBJ/SLAB     CACHE SIZE     NAME                   
67160160     66963905      99%        0.19K         3358008      20          13432032K     dentry                 
266252         232244      87%        0.10K       7196           37             28784K         buffer_head            
110608         110585      99%        0.98K          27652          4            110608K     ext4_inode_cache       
20272          20221      99%        0.03K        181          112         724K         size-32 
......       
# sort -k 2 -n /proc/slabinfo
# name            <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>
......
size-32            20173  20272     32  112    1 : tunables  120   60    8 : slabdata    181    181      0
ext4_inode_cache  110609 110612   1000    4    1 : tunables   54   27    8 : slabdata  27653  27653      0
buffer_head       232446 266252    104   37    1 : tunables  120   60    8 : slabdata   7196   7196      0
dentry            66980483 67191180    192   20    1 : tunables  120   60    8 : slabdata 3359559 3359559      0

可见,使用最多的就是 dentry。

dentry 和文件操作相关,一般是应用层频繁的文件操作导致内核频繁申请 dentry 内存所致。

那到底是哪些程序在大量进行文件操作呢?

# lsof | awk '{print $1}' | sort | uniq -c | sort -n
......
     96 Xorg
    100 gnome-pow
    102 gnome-ses
    122 metacity
    126 master
    170 gnome-set
    206 gdm-simpl
    273 sshd
    952 httpd
   1249 virt-who

近一步使用 lsof 命令观察具体程序打开了哪些文件进行分析。

解决办法

如果仅仅是 Slab 占用了过多内存,而 SUnreclaim 并不大,且没有持续增长,可以忽略。因为如果有需要,系统会自动释放出内存供其它程序使用。
如果 SUnreclaim 很大且不断增长,很有可能是内核 bug。
SReclaimable 都是 clean 的缓存,随时可以释放,所以也可以手动回收 Slab:

echo 2 > /proc/sys/vm/drop_caches

但是注意,手动清除缓存可能会在一段时间内降低系统性能,所以一般选择在业务低峰时间做。

后续

2020.11.03 22:20 清理后,多长时间又涨起来?
因为 httpd 中的应用已经无法改造,是否是 virt-who 造成的?等下一次问题再现时,在其中一个节点上停用观察之。

-- By 许望(RHCA、OCM、VCP)
最后修改:2020 年 11 月 16 日 06 : 05 PM
如果觉得我的文章对你有用,请随意赞赏