问题场景:

    今天早上收到报警,系统剩余内存低于15%;这台机器运行的服务为nginx,理论上占用的内存不会很多,于是进行排查;

    查看使用的内存:free -m

        Linux内存异常:活跃进程使用的内存远远低于实际使用的内存_内存

    查看活跃进程使用的内存:ps aux --sort -rss | head

        Linux内存异常:活跃进程使用的内存远远低于实际使用的内存_内存异常_02

    可以看到free -m使用的内存与前10个活跃进程使用的内存存在较在差距~

问题分析及解决:

    通过查阅资料,有可能是slab占用了内存;

    步骤一:安装atop工具,查看内存的具体使用情况

        yum install atop -y

        执行命令:atop

        Linux内存异常:活跃进程使用的内存远远低于实际使用的内存_Linux_03

        可以看到slab占用了2.6G的内存,确定了引用内存异常的原因是slab

    步骤二:查看slab占用内存的具体情况

        执行命令:cat /proc/meminfo | awk '{sum=$2/1024} {print $1 sum " MB"}' | grep claim

SReclaimable:3153.37 MB
SUnreclaim:25.0664 MB

        SReclaminable:表示可回收的slab占用的内存

        SUnreclaim:表示不可回收的slab占用的内存

        由此猜想,slab可回收的内存可以由系统进行自动回收或者手动回收

    步骤三:查看具体是什么占用了slab内存

        执行命令:slabtop

        Linux内存异常:活跃进程使用的内存远远低于实际使用的内存_内存异常_04

        注:可以看到是dentry占用大部分的slab内存

        

        slab内存分类:

            dentry:目录项缓存

            inode_cache:vfs索引节点缓存

      

        slab是Linux操作系统的一种内存分配机制。其工作是针对一些经常分配并释放的对象,您可以看看哪些应用进程的slab占用的内存比较多,是否这些应用需要频繁的请求和释放内存,比如进行一些小文件的读写。如果都是应用的正常使用,可以考虑升级服务器内存,如果内存不足影响业务,需要临时释放一下slab占用的内存; 

        

        slab内存的释放策略:

            相关内核参数:vm.drop_caches

            参数值含义:

                0:不做任何处理,由系统自己管理

                1:清空pagecache(页缓存)

                2:清空dentry和inode_cache(目录荐和索引节点缓存)

                3:清空pagecache、dentries和inode_cache

    步骤四:手动回收slab中的可回收内存

        #将内存的数据同步到磁盘

        sync

        #手动清空页缓存(pagecache)和元数据缓存(slab)

        sudo sysctl -w vm.drop_caches=3  或者 echo 3 > /proc/sys/vm/drop_caches

        #内存回收后把内核参数改为原来的值

        sudo sysctl -w vm.drop_caches=0  或者 echo 0 > /proc/sys/vm/drop_caches

        注:以上修改的方式是临时生效

相关内核参数:kswapd0

    kswapd0:专门的内核线程用来定期回收内存;

    为了衡量内存的使用情况,kswapd0定义了三个内存阈值(watermark,也称为水位),分别是:

        页最小阈值(pages_min)

        页低阈值(pages_low)

        页高阈值(pages_high)

    关系图:

        Linux内存异常:活跃进程使用的内存远远低于实际使用的内存_内存_05

    

    kswapd0定期扫描内存的使用情况,并根据剩余内存落在这三个阈值的空间位置,进行内存的回收操作。


    • 剩余内存小于页最小阈值,说明进程可用内存都耗尽了,只有内核才可以分配内存。

    • 剩余内存落在页最小阈值页低阈值中间,说明内存压力比较大,剩余内存不多了。这时kswapd0会执行内存回收,直到剩余内存大于高阈值为止。

    • 剩余内存落在页低阈值页高阈值中间,说明内存有一定压力,但还可以满足新内存请求。

    • 剩余内存大于页高阈值,说明剩余内存比较多,没有内存压力。

    

    vm.min_free_kbytes:保留给内核使用的;当到达min,系统会启动 kswapd0 进行内存回收;此参数的默认值为67584; 

    

    可以通过此参数来的值让系统自动回收slab,但是此参数不宜设置过大;

    

    由此此参数引发的血案:

        生产中某台机器调高了此参数为1G,当系统free的内存小于1GB时,观察到kswapd进程开始工作(进程状态从Sleeping变为Running),同时dentry_cache开始被系统回收,直到系统free的内存介于low阈值和high阈值之间,停止回收。

        如果系统的free内存一直低于1G,则kswapd0一直运行,造成系统持续较大压力!