一, 预备知识
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, 再继续, 代码和文档归档在: 归档 https://github.com/wacxt/TechnologyArchitecture .
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 中包含的是哪些?(匿名页还是文件映射)
四, 参考文献
- , http://linuxperf.com/?p=142
- ,
来源: https://juejin.im/post/5c128c01e51d45527852a7aa