恼人的Linux内存统计

简介: 前几天遇到一个客户反馈的内存问题,大致背景是自建的数据库业务,购买的是阿里云裸金属服务器总是遇到内存不足的情况,每次重启过不了多久就会不断报内存页分配失败,而且无论怎么扩容内存业务改善都不大

   前几天遇到一个游戏业务TOP客户反馈的问题,大致背景是自建的数据库业务,购买的是阿里云裸金属服务器总是遇到内存不足的情况,每次重启过不了多久就会不断报内存页分配失败,而且无论怎么扩容内存业务改善都不大,内存问题我们遇到过很多类,今天就就着这个案例来说一说内存使用的场景分析


    问题排查前我们先大致整理一下Linux内存使用分为几个大类,以及都如何去统计,以便与日后排查问题能有更好的方向性。

一. 查看总的内存使用情况  

通常我们是这样看内存的剩余情况的:

[root@iZm5ed****sZ ~]# free -m 
             total       used       free     shared    buffers     cached
Mem:          7856       2332       5524          0          7       2123
-/+ buffers/cache:        200       7655
Swap:            0          0          0

关于这部分信息的解读我们结合《Linux Performance and Tuning Guidelines》中的截图:

image.png

截图取自(page 47):https://lenovopress.com/redp4285.pdf

从上图结合我们的测试环境可以获取到的信息如下:

1、总内存7856M ,已用内存2332M

2、由于buffers + cached内存实际上也是可用内存,该内存也可以通过echo 3 > /proc/sys/vm/drop_caches 回收pagechae、dentries and inodes ,所以实际上已经使用的内存是200M, 剩余free内存 7655M 。

PS:buffers + cached 分别是什么不展开讨论了上面链接的文档中也有详细说明,free命令的统计方式也在改进,后面版本已经不单独统计-/+ buffers/cache行了,直接帮我们算好了。那么真正used部分消耗在哪里了呢?


二. RSS 部分消耗

RSS(resident set size) 也就是每个进程用了具体的多少页的内存,linux内存管理中进程的代码,库,堆和栈都会消耗内存,但是申请出来的内存,只要没真正touch过,是不算的,这里怎么理解呢,例如我调用malloc申请一段内存,此时只在虚拟内存段中分配了,实际这一段空间并没有映射真正的物理内存,只有当你尝试写这个内存页时系统才会通过缺页异常为这个虚拟内存页映射物理内存。进程实际占用的物理内存我们只需要统计下RSS就可以了,我们可以从/proc/PID/status 里获取到每个进程的rss,其实ps,top等工具也是从/proc下面统计的:

[root@iZm5****rsZ ~]# cat /proc/1686/status 
Name: assist_daemon
State:  S (sleeping)
Tgid: 1686
Pid:  1686
PPid: 1
TracerPid:  0
Uid:  0 0 0 0
Gid:  0 0 0 0
Utrace: 0
FDSize: 256
Groups: 
VmPeak:    17968 kB
VmSize:    17828 kB
VmLck:         0 kB
VmHWM:      2192 kB
VmRSS:      2192 kB
VmData:    15064 kB
VmStk:        84 kB
VmExe:      2628 kB
VmLib:         0 kB
VmPTE:        52 kB
VmSwap:        0 kB
Threads:  8

同时为了统计方便/proc/PID/statm 第二列就是RSS内存使用page页的多少,而在linux下默认使用的page页大小是4KB 。所以我上面计算求和后,最后乘以4就是最终的内存大小,我们写个脚本遍历一下:

[root@iZm****rsZ ~]# cat test.sh 
#/bin/bash                                                                                                              
for PROC in `ls  /proc/|grep "^[0-9]"`
do
  if [ -f /proc/$PROC/statm ]; then
      TEP=`cat /proc/$PROC/statm | awk '{print ($2)}'`
      RSS=`expr $RSS + $TEP`
  fi
done
RSS=`expr $RSS \* 4`

三. slab内存消耗部分

slab分配器,是为了弥补buddy system分配器最小分配粒度是以物理页帧(page)为单位进行管理的缺陷,内核中有大量的数据结构只需要若干bytes的空间,倘若仍按页来分配,势必会造成大量的内存被浪费掉。那这部分怎么统计呢?我们可以先看下slabtop命令,可以理解为slab版本的top:

image.png

slab内存的消耗我们可以查看/proc/slabinfo文件,具脚本为:

[root@iZm5e****sZ ~]# echo `cat /proc/slabinfo |awk 'BEGIN{sum=0;}{sum=sum+$3*$4;}END{print sum/1024/1024}'` MB
88.8909 MB

四、PageTables内存消耗

通俗来讲,虚拟内存的管理的核心是解决如何在小的物理内存中运行更大程序的问题

在Linux中,解决这个问题的关键是一个叫做page table[PT页面转换表]的结构。Linux把物理内存分为了固定统一大小的块,称为page[页],一般为4KB,并且每个页都有一个编号 [page frame number]。这样一个512M大小的内存将包括128K个页。这种方式称为paging,使得操作系统对内存的管理更方便。page table的作用就是将进程操作的地址[虚拟地址]转换成物理地址。

那么去哪里看PageTables占用多少呢:

[root@iZm5****rsZ ~]# echo `grep PageTables /proc/meminfo | awk '{print $2}'` KB
2872 KB

把上面几个脚本合计一下:

[root@ali-test ~]# cat test.sh 
#/bin/bash
for PROC in `ls /proc/|grep "^[0-9]"`
do
  if [ -f /proc/$PROC/statm ]; then
      TEP=`cat /proc/$PROC/statm | awk '{print ($2)}'`
      RSS=`expr $RSS + $TEP`
  fi
done
RSS=`expr $RSS \* 4`
PageTable=`grep PageTables /proc/meminfo | awk '{print $2}'`
SlabInfo=`cat /proc/slabinfo |awk 'BEGIN{sum=0;}{sum=sum+$3*$4;}END{print sum/1024/1024}'`
echo $RSS"KB", $PageTable"KB", $SlabInfo"MB"
printf "rss+pagetable+slabinfo=%sMB\n" `echo $RSS/1024 + $PageTable/1024 + $SlabInfo|bc`
free -m
[root@ali-test ~]# sh test.sh 
310420KB, 5060KB, 93.2412MB
rss+pagetable+slabinfo=400.2412MB
              total        used        free      shared  buff/cache   available
Mem:           3789         193        2146           0        1449        3336
Swap:             0           0           0

这里我们会发现一个问题,我们计算的总内存占用400.2412M ,free 统计的used内存193M,首先这里free计算的used啥也不是,是直接减出来的:total - free - buffers - cache(这里buffer和cache都有一部分是不可回收的所以减多了具体可以看/proc/meminfo)

但是即使这样依然有差距,相差的到底是哪一部分?一般我们认为是shared内存重复统计导致的,我们在计算RSS时由于RSS包含了共享内存部分(RSS = total_RSS+files+share_mm),所以可能存在部分share内存被重复累加了,以sshd为例可以看到很多so等共享库包含在内:

[root@ali-test ~]# pmap  1046
1046:   /usr/sbin/sshd -D
0000565429212000    800K r-x-- sshd
00005654294d9000     16K r---- sshd
00005654294dd000      4K rw--- sshd
00005654294de000     36K rw---   [ anon ]
000056542b414000    132K rw---   [ anon ]
00007ff927ec6000     48K r-x-- libnss_files-2.17.so
00007ff927ed2000   2044K ----- libnss_files-2.17.so
00007ff9280d1000      4K r---- libnss_files-2.17.so
00007ff928b18000      4K r---- libkrb5support.so.0.1
00007ff928b19000      4K rw--- libkrb5support.so.0.1
00007ff928b1a000      8K r-x-- libfreebl3.so
00007ff928b1c000   2044K ----- libfreebl3.so
00007ff928d1b000      4K r---- libfreebl3.so
00007ff928d1c000      4K rw--- libfreebl3.so
00007ff928d1d000    232K r-x-- libnspr4.so
00007ff928d57000   2044K ----- libnspr4.so
00007ff928f56000      4K r---- libnspr4.so
00007ff928f57000      8K rw--- libnspr4.so
00007ff928f59000      8K rw---   [ anon ]
00007ff929d39000      4K rw---   [ anon ]
.
.
.
.
.
.
00007ff929d3a000    412K r-x-- libssl.so.1.0.2k
00007ff929da1000   2048K ----- libssl.so.1.0.2k
00007ff929fa1000     16K r---- libssl.so.1.0.2k
00007ff929fa5000     28K rw--- libssl.so.1.0.2k
00007ff929fac000    112K r-x-- libsasl2.so.3.0.0
00007ff929fc8000   2044K ----- libsasl2.so.3.0.0
00007ff92a1c7000      4K r---- libsasl2.so.3.0.0
00007ff92a1c8000      4K rw--- libsasl2.so.3.0.0
00007ff92a1c9000     92K r-x-- libpthread-2.17.so
00007ff92a1e0000   2044K ----- libpthread-2.17.so
00007ff92a3df000      4K r---- libpthread-2.17.so
00007ff92a3e0000      4K rw--- libpthread-2.17.so
00007ff92a3e1000     16K rw---   [ anon ]
00007ff92a3e5000     84K r-x-- libgcc_s-4.8.5-20150702.so.1
00007ff92a3fa000   2044K ----- libgcc_s-4.8.5-20150702.so.1
00007ff92a5f9000      4K r---- libgcc_s-4.8.5-20150702.so.1
00007ff92a5fa000      4K rw--- libgcc_s-4.8.5-20150702.so.1
00007ff92a5fb000    312K r-x-- libdw-0.176.so
00007ff92a649000   2048K ----- libdw-0.176.so
00007ff92a849000      8K r---- libdw-0.176.so
00007ff92a84b000      4K rw--- libdw-0.176.so
00007ff92a84c000     16K r-x-- libgpg-error.so.0.10.0
00007ff92a850000   2044K ----- libgpg-error.so.0.10.0
00007ff92aa4f000      4K r---- libgpg-error.so.0.10.0
00007ff92aa50000      4K rw--- libgpg-error.so.0.10.0
00007ff92aa51000    500K r-x-- libgcrypt.so.11.8.2
00007ff92aace000   2044K ----- libgcrypt.so.11.8.2
00007ff92accd000      4K r---- libgcrypt.so.11.8.2
00007ff92acce000     12K rw--- libgcrypt.so.11.8.2
00007ff92acd1000      4K rw---   [ anon ]
00007ff92acd2000     80K r-x-- liblz4.so.1.7.5
00007ff92ace6000   2044K ----- liblz4.so.1.7.5
00007ff92aee5000      4K r---- liblz4.so.1.7.5
00007ff92aee6000      4K rw--- liblz4.so.1.7.5
00007ff92aee7000    148K r-x-- liblzma.so.5.2.2
00007ff92be9c000      8K rw---   [ anon ]
00007ff92be9e000   1800K r-x-- libc-2.17.so
00007ff92c060000   2048K ----- libc-2.17.so
00007ff92c260000     16K r---- libc-2.17.so
00007ff92c264000      8K rw--- libc-2.17.so
00007ff92c266000     20K rw---   [ anon ]
00007ff92ebaa000    136K r-x-- ld-2.17.so
00007ff92edaa000     92K rw---   [ anon ]
00007ff92edca000      4K rw---   [ anon ]
00007ff92edcb000      4K r---- ld-2.17.so
00007ff92edcc000      4K rw--- ld-2.17.so
00007ff92edcd000      4K rw---   [ anon ]
00007fffd2868000    132K rw---   [ stack ]
00007fffd299b000      8K r-x--   [ anon ]
ffffffffff600000      4K r-x--   [ anon ]
 total           112880K
[root@ali-test ~]# cat /proc/1046/statm 
28219 1091 833 200 0 190 0

上面有两个结论:

1. statm中第二段是RSS = 1091 pages ,第三段是share = 833 pages,RSS包含了share,但是和pmap中计算的total为什么差这么多呢,因为total里把每个so的所有内存都算上了,而进程真正占用的部分是遵从最小化原则的就是调用了的部分才会映射进来,所以实际以statm里的第三段为准。

2. 从用户态很难严格区分每个共享内存被几个进程占用,从而去抵消重复计算部分,所以共享内存存在计算误差。

    结合上面的内容,我们介入后发现了几个特征,其一从meminfo看业务报错时其实free内存还有好几个G,其二系统日志中在不断打印页申请失败(page allocateion failure)如下:

网络异常,图片无法展示
|

从这里可以看出,客户环境有大量的4K free页,但是4K以上的页已经全为0了,很明显齐内存碎片化非常严重,此时申请大页内存基本都会失败,说明客户业务在频繁申请释放4K页打散了大页内存。同时可以看到pagetable快接近150G了,说明虚拟内存页表非常大明显是不正常或者说不健康的。


1. echo 1 >/proc/sys/vm/compact_memory (手动触发一次内存规整,会合并部分可移动内存页,是有损的因为要遍历内存所以一般线上环境不推荐)

2. /proc/sys/vm/extfrag_threshold 调整这个默认值,默认是500,可以尝试调整到200,表示系统在内存碎片的处理倾向于做memory compaction,迁移合并及时的话,就可以缓解碎片,这个最好是在开机后默认配置,不要等出问题后再配。

3. sync; echo 3 >/proc/sys/vm/drop_caches 主动回收cache,只是缓解一下不解决问题。

4.  vm.zone_reclaim_mode , 我们线上的主机中该参数均为默认值 0, 不会触发 reclaim 操作而是直接返回 zone full ,所以改为1,但是如果做为文件服务器的话建议改为0,因为这种场景大部分内存应该都需要用于文件系统缓存来提高IO响应时间。

客户业务主要为自建oracle数据库,且没有启用hugepage,大内存部署oracle还是推荐使用大页,这是最大的一个问题:

1. 配置数据库使用大页,配置sga_max_size

2. 设置系统预留大页内存:vm.nr_hugepages=xxx(hugepage是2M,pages换算的总大小要大于数据库定义的sga_max_size)

3. 配置memlock数量要大于大页的数量 -1代表不限制:/etc/security/limits.conf

oracle   soft   memlock    -1

oracle   hard   memlock    -1


目录
相关文章
|
5天前
|
Linux
Linux rsyslog占用内存CPU过高解决办法
该文档描述了`rsyslog`占用内存过高的问题及其解决方案。
28 4
|
28天前
|
移动开发 运维 监控
掌握Linux运维利器:查看CPU和内存占用,轻松解决性能问题!
掌握Linux运维利器:查看CPU和内存占用,轻松解决性能问题!
|
28天前
|
移动开发 Linux
Linux下如何查看哪些进程占用的CPU内存资源最多
Linux下如何查看哪些进程占用的CPU内存资源最多
|
10天前
|
机器学习/深度学习 缓存 监控
linux查看CPU、内存、网络、磁盘IO命令
`Linux`系统中,使用`top`命令查看CPU状态,要查看CPU详细信息,可利用`cat /proc/cpuinfo`相关命令。`free`命令用于查看内存使用情况。网络相关命令包括`ifconfig`(查看网卡状态)、`ifdown/ifup`(禁用/启用网卡)、`netstat`(列出网络连接,如`-tuln`组合)以及`nslookup`、`ping`、`telnet`、`traceroute`等。磁盘IO方面,`iostat`(如`-k -p ALL`)显示磁盘IO统计,`iotop`(如`-o -d 1`)则用于查看磁盘IO瓶颈。
|
1天前
|
存储 Web App开发 运维
|
1天前
|
算法 安全 Linux
深度解析:Linux内核内存管理机制
【4月更文挑战第30天】 在操作系统领域,内存管理是核心功能之一,尤其对于多任务操作系统来说更是如此。本文将深入探讨Linux操作系统的内核内存管理机制,包括物理内存的分配与回收、虚拟内存的映射以及页面替换算法等关键技术。通过对这些技术的详细剖析,我们不仅能够理解操作系统如何高效地利用有限的硬件资源,还能领会到系统设计中的性能与复杂度之间的权衡。
|
2天前
|
弹性计算 运维 Shell
统计Linux 进程相关数量信息
【4月更文挑战第29天】
5 0
|
2天前
|
弹性计算 运维 Shell
统计 Linux 进程相关数量信息
【4月更文挑战第29天】
6 1
|
3天前
|
监控 Linux
【专栏】如何检查 Linux 内存使用量是否耗尽?这 5 个命令堪称绝了!
【4月更文挑战第28天】在 Linux 中,掌握检查内存使用情况至关重要,因为内存问题可能导致系统性能下降甚至崩溃。本文介绍了 5 个常用的检查内存命令:1) `free` 提供内存和交换区的详细信息;2) `top` 显示进程信息及内存使用;3) `vmstat` 输出系统综合信息,包括内存动态变化;4) `pidstat` 监控特定进程的内存使用;5) `/proc/meminfo` 文件提供系统内存详细数据。了解和使用这些命令能帮助用户及时发现并解决内存相关问题,确保系统稳定运行。
|
8天前
|
消息中间件 Linux
【linux进程间通信(二)】共享内存详解以及进程互斥概念
【linux进程间通信(二)】共享内存详解以及进程互斥概念