内存问题往往是线上环境最容易导致的问题,因为其实对于程序来说,内存总是不够用的。而大多数我们在线上遇到的问题总是一个叫 OOM 的,导致这个问题的原因也有很多,今天我们就来看看,如何在线上定位或者排查这样的问题。
free -h
[root@Linkin /]# free -h
total used free shared buff/cache available
Mem: 7.6G 326M 6.2G 480K 1.1G 7.0G
Swap: 0B 0B 0B
这个命令可以看到当前设备的内存总体使用情况,以及很清楚的看到交换区的内存使用情况
top
+ M
top - 20:29:20 up 10 min, 1 user, load average: 0.00, 0.09, 0.10
Tasks: 92 total, 1 running, 91 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.2 us, 0.2 sy, 0.0 ni, 99.5 id, 0.2 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 7.6/8009024 [|||||||| ]
KiB Swap: 0.0/0 [ ]
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1001 influxdb 20 0 566444 81360 16180 S 0.3 1.0 0:11.26 influxd
1025 root 20 0 799412 73760 27816 S 0.0 0.9 0:00.76 dockerd
1023 root 20 0 509024 40648 14164 S 0.0 0.5 0:00.37 containerd
1009 root 20 0 574204 17504 6128 S 0.0 0.2 0:00.32 tuned
746 polkitd 20 0 612344 12260 4772 S 0.0 0.2 0:00.03 polkitd
top 总是能发现绝大多数的问题,按 M 之后会按照内存的使用情况进行排序,你可以清楚的看到内存占用最多的进程是什么。
vmstat -a 5 5
[root@Linkin ~]# vmstat -a 5 5
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 0 5970260 487444 1006228 0 0 1 3 4 8 0 0 100 0 0
0 0 0 5970260 487448 1006336 0 0 0 30 588 1132 0 0 100 0 0
这个命令可以清楚看到当前是否有交换区的换入和换出,并且能记录下变化过程,还有活跃和非活跃的内存使用情况;同时能看到 CPU 和 IO 的情况,顺便看看是否由其他问题导致。
其实内存问题的排查并没有特别复杂,我们所要做的就是定位:
那下面就说说我一般在线上排查问题常用的步骤,仅供参考
内存的出现问题的原因有很多,大多数都和具体业务相关,这里也没有办法进行罗列,举几个最常见的案例
一般是由于数据量过大,比如查询数据时没有约束最大值导致将数据库全部数据都查询出来;
或者是由于传递参数问题,比如传递了一个 10000000 这样的值,然后用这个长度直接去创建了数组或者别的类型,而实际并没有那么多数据,经常出现于三方调用接口时导致
如果是使用 java 或者 go 这样带有 gc 的语言会好很多,你不用主动去 free 你使用的内存;但是别妄想着没问题,实际中很多时候用于指针的使用,或者是线程的不断创建,等等原因导致对象无法被 gc 从而也会产生内存泄露。
还有连接池没有设置最大上限,也会有可能慢慢变大。
有时候应用会缓存一些数据到内存中,一般情况下不会缓存很大的数据,可能就是一些热点数据等,大多时候缓存大数据量的时候也会考虑使用 redis,但还是会出现使用内存缓存一些 map 的时候由于用户量突然上来,导致内存占用过多的情况发生。
有时候一些应用访问并不多,但是内存占用往往在一段时间之后就会变大且无法释放,有些时候就是由于内部的一些定时任务或者定时器导致内存使用后没还导致的。多见于一些需要设定超时时间,但是超时时间又没有设定默认值的情况。
有时内存突然的上升会导致内核被迫开始使用 swap,那么使用 swap 其实就已经意味着你正在危险的边缘徘徊了,而且使用 swap 往往总伴随一些 IO 问题。附赠一个命令swapoff -a && swapon -a
这个不是个问题,但是需要提一句,有的时候有人会看到 VIRT 占用很多,或者缓存和缓冲占用很多,其实这个问题都不大。虚拟内存你占了但是并没有实际分配,缓存和缓冲往往都是内核为了加快访问而做的,当内存不够时会主动进行释放不用担心。
内存问题一般就两种:
当出现内存问题时还是要多加注意,针对不同的语言也有不同的处理思路,java 看看虚拟机,go 看看 pprof 等等。