一,预备知识
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中包含的是哪些?(匿名页还是文件映射)