Linux工具性能调优系列三:swap问题定位

4,388 阅读8分钟

一,预备知识

1.1 什么是swap

swap当我们指的名词的时候,它可以是一个分区,也可以是一个文件,是操作系统中一个存放从内存中置换出的数据的地方。 当我们指的是一个动词时候,代表的是从物理内存交换数据到swap分区这个动作。

1.2 为什么会swap

(1) 当物理内存不够用时候,会根据特定的算法,把一部分内存交换到swap分区(此时还会伴随着高IO)。但是并不是所有的内存都可以被交换到swap分区。 (2) kswapd进程周期性对内存进行检查,如果发现高于水位线,则触发swap,此举是为了不让系统剩余内存很少,防止出现突然的大内存申请。这块暂不深入讲解,后续再补充。

1.3 swap的到底是什么

首先我们要知道,内存管理将内存分为active和inactive,进程用户空间使用的映射包括了匿名映射(anon)和文件映射(file)。所有一共有active anon,inactive anon,active file,inactive file。对于文件映射,由于本身是磁盘空间中的文件,所有它不会被swap,当需要释放时候,脏数据直接写回磁盘,其他数据直接释放即可。内存交换到swap,肯定是交换不活跃的数据,所有,inactive anon是最主要的被交换的内存。那么对于操作系统来说,当我需要回收内存时候,你说它是针对文件映射好,还是针对匿名映射好,这就涉及到了一个参数:swapiness

1.3.1 swapiness

swapiness是设置内存回收时候,更倾向于回收文件映射还是匿名映射,在/proc/sys/vm/swappiness设置值。对于swapiness=100,那么两者之间的权重是一致的,值越小,越倾向于回收文件映射,不过如果达到系统高水位线,还是会swap,除非直接使用swapoff -a等手段关闭系统swap。

1.3 swap的好坏

swap的好处是当内存不足时候,可以将一部分交换出去,不会触发oom-killer。跑得慢总比不能跑好。 swap的坏处是交换时候,会触发高IO,同时会降低系统的性能。对于我们隔离做的不好的时候,会影响到其他应用的性能。

二,工具选择

一个工具往往具有多种用途,但是本文只说明针对swap问题

工具名称 使用姿势 采集指标来源
free free -h /proc/meminfo
top 按f,选择swap /proc/$pid/smaps
vmstat vmstat /proc/meminfo
iotop iotop
iostat iostat -xdm
pidstat pidstat -d 1 /proc/$pid/io

三,案例分析

3.1 应用一直申请内存

本次的案例是使用golang编写,在一个死循环里面,每次循环申请内存,并且不释放,然后达到一定次数后释放内存,等待GC,再继续,代码和文档归档在:归档

3.1.1 运行程序和分析

(1) 运行

root@szdc-calic-2-6:/www/linuxperformancetool/swap# ./swapexample1

由于上面说的命令都可以用于分析,大家根据喜好搭配使用即可。我这里用top,vmstat和pidstat搭配使用进行分析,可以开多几个终端一起看。

(2) 分析 首先是使用vmstat,从下面可以看出,当程序占用内存越来越大时候,出现了很高的swap io和block io,想一下,为什么这两个同时都增高?

root@szdc-calic-2-6:~# vmstat -a 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free  inact active   si   so    bi    bo   in   cs us sy id wa st
 1  0 1240208 18816440 715216 12702360    0    0     1    18    0    0  0  0 99  0  0
 1  0 1240208 18086504 715220 13431988    0    0     0     0 2223 3192  1  4 96  0  0
 1  0 1240208 17343176 715220 14173444    0    0     0    60 1906 3099  1  4 95  0  0
 1  0 1240208 16618392 715260 14897140    0    0     0    60 2351 3552  1  4 96  0  0
 1  0 1240208 15883220 715280 15632572    0    0     0     0 2187 3547  1  4 96  0  0
 0  2 1240300 15276012 806532 16151868    0   92     0  2088 2365 3373  1  3 95  2  0
 0  2 1265792 15270100 2023920 14946200   56 25500   100 27536 5102 9189  1  1 93  5  0
 0  3 1265784 15274476 2023856 14944732   40    0    40  2572 2162 2425  0  0 90 10  0
 0  9 1287036 15276116 2022816 14944232   88 21276    88 22228 2074 3136  0  0 79 21  0
 0  9 1287020 15273676 2024776 14946436   92    0    92  2288 3563 5822  0  0 79 20  0
 2  3 1286912 15271708 2024844 14946532  128    0   128  1432 2989 4907  0  0 84 16  0
 0  3 1286912 15271692 2024572 14946520    0    0     0  2344 3098 4543  0  0 87 13  0
 0  4 1312096 15265788 2022444 14951064    0 25188     0 26628 4946 9752  1  1 90  9  0

发现了系统问题后,我们就需要对问题进行定位了,这里可以使用top,pidstat,iotop等工具进行定位,我这边直接使用top,按f选择swap。

top - 00:52:38 up 253 days, 14:23,  3 users,  load average: 6.44, 2.60, 1.23
Tasks: 359 total,   1 running, 358 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.6 us,  0.6 sy,  0.0 ni, 67.0 id, 31.7 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem : 32895096 total,   236100 free, 32011520 used,   647476 buff/cache
KiB Swap: 31250428 total, 28358956 free,  2891472 used.   261616 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                               SWAP
45466 root      20   0 10.076g  21624   2520 S  12.9  0.1   7881:36 etcd                                                 19636
  146 root      20   0       0      0      0 D   9.2  0.0   2:28.44 kswapd0                                                  0
14179 root      20   0 31.136g 0.029t   4316 S   3.3 94.8   0:31.86 main1                                               1.375g
  147 root      20   0       0      0      0 S   1.7  0.0   1:52.23 kswapd1                                                  0
10737 root      20   0 3067756  60688   5968 S   0.7  0.2   3364:41 dockerd                                              60380
10750 root      20   0 2898452  38724   3096 S   0.7  0.1 629:49.22 docker-containe                                      79288

从top的变化可以看出,pid=14179的进程一直swap一直在增高,而且内存占用越来越高,其他进程虽然有出现swap增多,但是内存使用并没有增高,可以判断,该进程是导致出现swap的原因。那么,我们再进一步进行确认。

使用pidstat判断该pid的io情况,可以看出,是存在很大的IO

root@szdc-calic-2-6:~# pidstat -p 14179 -d 1
Linux 4.4.0-87-generic (szdc-calic-2-6.meitu-inc.com) 	Wednesday, December 12, 2018 	_x86_64_	(24 CPU)

12:55:40 CST   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
12:55:41 CST     0     14179  71920.00      0.00      0.00       0  main1
12:55:42 CST     0     14179  72796.04      0.00      0.00       0  main1
12:55:43 CST     0     14179  85664.00      0.00      0.00       0  main1
12:55:44 CST     0     14179  78128.00      0.00      0.00       0  main1
12:55:45 CST     0     14179  69660.00      0.00      0.00       0  main1
12:55:46 CST     0     14179  59892.00      0.00      0.00       0  main1

3.1.2 问题

这些问题都不会直接进行解答,实在想不出来的,可以到归档项目下面提issue或者在下面评论

(1) 为什么例子中,只是简单的申请内存,会造成swap io和block io同时增高? (2) 例子中,明明还有剩余内存未被使用,可是已经开始频繁进行swap和回收大量内存。 (3) golang gc时候,会把已经swap出去的内存再swap到物理内存中,再进行gc吗? (4) 上一节中的buffer和cache中包含的是哪些?(匿名页还是文件映射)

四,参考文献

1, linuxperf.com/?p=142

2, cloud.tencent.com/developer/a…