
1、前言
从前面几篇文章,我们已经谈了内存的原理、swap的使用、缓存的类型,还有如果内存发生泄漏应该使用什么工具来排查,现在已经对内存有了基础的认识。如果在工作学习中遇到了内存问题,应该怎么排查或者从哪里入手呢,文章就主要谈一下内存问题的排查思路。
一般情况下,我们怀疑出现内存问题时,首先会有内存方面的告警指标提醒我们,也就是这些最基础的指标会第一时间反映出问题。有了指标,我们可以选择合适的工具再进一步的分析。根据工具深入分析后一般都能够很快判断出问题在哪里,然后进行处理。
2、内存指标
根据前文的描述,我们需要关心的内存指标,如下
系统内存指标: 总内存、空闲内存、已用内存、缓存(buffer/cache)使用量、缓存命中率、缺页异常、slab可回收内存等。
进程内存指标:
虚拟内存(VIRT)、常驻内存/物理内存(RES/RSS)、共享内存(SHR)、进程内存使用百分比(%MEM)、独占内存(约等于RES -
SHR)、进程使用的虚拟内存大小(VSZ)等。
swap内存: 总内存、已用内存、剩余内存、换出换入速度。
3、查看工具
了解了上面这些指标,我们需要相应的工具及时的查看到准确的数据,来进行判断问题出现哪里。
root@test:~# free -wh
total used free shared buffers cache available
Mem: 1.0Ti 48Gi 803Gi 3.0Mi 2.0Gi 152Gi 954Gi
Swap: 39Gi 0B 39Gi
free是查看内存最常用的工具,可以快速看到内存的使用情况,用了多少,还剩余多少。
各列含义:
total: 总内存量
used: 已使用的内存量(used = total - free - buffers - cache)
free: 未被使用的内存量
shared: 多个进程共享的内存总量(通常为0,或者很小)
buffers: 内核缓冲区使用的内存量
cache: 页面缓存和slab使用的内存量
available: 估计可用于启动新应用程序的内存量(不含交换分区)
top - 11:34:44 up 526 days, 20:11, 1 user, load average: 0.84, 0.92, 1.04
Tasks: 1343 total, 1 running, 1342 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.4 us, 0.0 sy, 0.0 ni, 99.6 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
MiB Mem : 1031349.+total, 823056.8 free, 50146.1 used, 158146.7 buff/cache
MiB Swap: 40960.0 total, 40960.0 free, 0.0 used. 976896.4 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
4095 mysql 20 0 6189264 1.6g 550648 S 21.5 0.2 204441:48 mysqld
2732 root 20 0 141524 34936 9324 S 17.2 0.0 194545:07 node_exporter
1129876 root 20 0 7095304 1.1g 54208 S 5.6 0.1 1770:30 prometheus
2936909 root 20 0 5411276 231104 56112 S 2.0 0.0 19108:45 wsssr_defence_s
top命令也是查看内存的重要工具,可以实时看到内存的变化情况。更详细的可以使用htop命令,需要单独安装。
1)第四行(内存):
MiB Mem : 总内存。
free:空闲内存MiB。
used:已使用内存 MiB。
buff/cache:用作缓冲缓存的内存 MiB。
2)第五行(交换分区):
MiB Swap: 总交换分区。
free:空闲交换分区 MiB。
used:已使用交换分区MiB。
avail Mem:可用内存估计数(包括未使用的和缓冲缓存中可回收的)MiB。
3)进程实时展示
PID:进程ID。
USER:进程所有者。
PR:优先级。
NI:Nice值,负值表示高优先级,正值表示低优先级。
VIRT:虚拟内存使用量(单位KiB)。
RES:常驻内存大小(单位KiB)。
SHR:共享内存大小(单位KiB)。
S:进程状态(S=睡眠,R=运行,等)。
%CPU:CPU使用百分比。
%MEM:内存使用百分比。
TIME+:进程使用的CPU时间总和,精确到百分之一秒。
COMMAND:进程名称。
root@test:~# ps -aux |head -5
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.1 0.0 171128 13972 ? Ss 2024 1505:02 /sbin/init nopti
root 2 0.0 0.0 0 0 ? S 2024 8:41 [kthreadd]
root 3 0.0 0.0 0 0 ? I< 2024 0:00 [rcu_gp]
root 4 0.0 0.0 0 0 ? I< 2024 0:00 [rcu_par_gp]
使用ps命令可以查看进程的内存使用情况。
各列的含义如下:
USER: 进程的所有者(哪个用户启动的)
PID: 进程ID
%CPU: 进程占用CPU的百分比
%MEM: 进程占用内存的百分比
VSZ: 进程使用的虚拟内存大小(单位KB)
RSS: 进程使用的物理内存大小(单位KB)
TTY: 进程在哪个终端上运行(如果为?则表示与终端无关)
STAT: 进程状态(例如:S-睡眠,R-运行,Z-僵尸等)
START: 进程启动时间
TIME: 进程占用CPU的总时间
COMMAND: 启动进程的命令
root@test:~# cat /proc/meminfo
...
Slab: 24081144 kB
SReclaimable: 18618148 kB
SUnreclaim: 5462996 kB
...
AnonHugePages: 3555328 kB
...
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
/proc/meminfo是一些特殊内存指标的重要位置,可以查看slab内核内存、配置的大页内存。
SReclaimable:可回收Slab,可被回收的内核缓存
SUnreclaim:不可回收Slab,内核必须保留的内存
AnonHugePages:匿名大页,透明大页使用的内存
HugePages_Total:空闲大页数,未使用的大页数量
HugePages_Free:总大页数,配置的大页数量
Hugepagesize:大页大小,每个大页2MB
root@test:~# vmstat 1 2
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 842544896 2081680 160133632 0 0 0 4 0 0 1 0 99 0 0
2 0 0 842544192 2081680 160133744 0 0 0 92 3923 5803 2 0 98 0 0
vmstat工具可以查看内存指标的动态变化,1表示时间间隔,2表示次数。
procs(进程):
r: 运行队列中的进程数(正在运行和等待CPU的进程数)
b: 等待I/O的进程数(处于不可中断睡眠状态的进程数)
memory(内存,单位:KB):
swpd: 已使用的虚拟内存大小(交换分区,KB)
free: 空闲的物理内存大小(KB)
buff: 用作缓冲区的内存大小(KB)
cache: 用作缓存的内存大小(KB)
swap(交换分区,单位:KB/秒):
si: 从磁盘读入交换分区的数据量(KB/秒)
so: 从交换分区写到磁盘的数据量(KB/秒)
io(IO,单位:块/秒):
bi: 从块设备接收的块数(块/秒,读磁盘)
bo: 发送到块设备的块数(块/秒,写磁盘)
system(系统):
in: 每秒的中断数(包括时钟中断)
cs: 每秒的上下文切换次数
cpu(CPU使用率,百分比):
us: 用户进程占用CPU时间百分比
sy: 内核进程占用CPU时间百分比
id: 空闲CPU时间百分比
wa: 等待I/O的CPU时间百分比
st: 被偷走的CPU时间百分比(一般是在虚拟化环境中,其他虚拟机占用的CPU时间)
root@test:~# sar -r 1 3
Linux 5.4.0-59-generic (jnai1asan01) 11/05/2025 _x86_64_ (128 CPU)
02:36:42 PM kbmemfree kbavail kbmemused %memused kbbuffers kbcached kbcommit %commit kbactive kbinact kbdirty
02:36:43 PM 842489592 1000321788 45882776 4.34 2081692 141547176 53245784 4.85 142730504 44375344 1200
02:36:44 PM 842489340 1000321652 45883056 4.34 2081692 141547236 53245784 4.85 142730656 44375460 1260
02:36:45 PM 842489772 1000322360 45882228 4.34 2081692 141547508 53245784 4.85 142730568 44375676 1536
sar -r可以查看系统内存的具体情况,同时重点查看缓存使用、账页情况。
kbmemfree: 空闲的物理内存量(KB)。
kbavail: 可用的内存量(KB),这是一个估计值,包括空闲内存和可回收的缓存(不包括交换空间)。
kbmemused: 已使用的物理内存量(KB)。注意,这个值可能包括被内核使用的内存,包括缓存和缓冲区。
%memused: 已使用内存的百分比。
kbbuffers: 内核缓冲区占用的内存量(KB)。
kbcached: 内核缓存数据占用的内存量(KB)。
kbcommit: 当前工作负载所需的内存总量(KB)。这是估计值,包括已使用的内存和尚未使用但预计需要的内存(如malloc分配但未实际使用的)。
%commit: kbcommit与总内存(包括交换空间)的百分比。
kbactive: 活跃内存量(KB),即最近被访问的内存。
kbinact: 非活跃内存量(KB),即最近未被访问的内存,可能被回收用于其他用途。
kbdirty: 等待写回磁盘的脏内存量(KB)。
除了上面几个常用的工具之外还有cachetop(查看进程缓存和缓冲区命中率信息)、pmap(查看具体进程的分布情况)。同时还有总要的内存泄露检查工具memleak-
bpfcc和valgrind。
4、内存问题排查思路
当前我们了解了内存的相关指标,并且也了解了通过工具获取这些指标的数值。从上面的工具介绍看,这些指标并不是孤立独自存在的,而是交叉存在,这也说明系统底层的内存原理是固定的,各种工具都基于这些原来,在某一些角度来反应内存的使用情况。
当发现内存问题时,我们总不能挨个拿工具去把所有指标都找出来,这样既浪费时间,也没有必要。根据内存指标的特性,从全局到局部、分层次的来分析内存问题,会是更好的选择。我们来总结分析一种比较好的排查思路。
首先
内存问题的最直接表现就是使用的过多了,不符合预期。一般都首先用free、top命令看一下内存使用的整体情况,并不是剩余内存变少了就一定是问题,也可以是缓存占用了。当发现缓存占用了很多内存,可以使用vmstat
或者 sar观察一下缓存的变化趋势,确认是否存在不合理的增长。
如果缓存持续增加,可以使用cachetop或者slabtop,还有/proc/meminfo,看一下这些缓存都被哪些进程占用了。找到进程后,再使用pmap分析一下进程中内存的使用情况。
然后
如果缓存使用正常,观察一下swap是否已经参与使用,如果参与,一是可能内存配置不足,用vmstat观察一下swap的变化量,若swap在不断地换入换出,说明确实是内存配置不足了。再就是可能有进程的内存在大幅增长,导致整体内存使用在不断增长,可以使用top和ps工具找到相关进程分析。
再就是
若内存增长无法得到缓解,且不断增加还发生过OOM的情况,那可以判断可能有进程发生内存溢出了,这时可以根据前面的步骤找到相关进程,然后用memleak-
bpfcc和valgrind来分析一下泄露的进程和堆栈。
最后
有时候内存问题也可能并不是程序引起的,也有可能是硬件故障,例如网卡故障导致数据传输异常从而引发内存缓存升高。或者磁盘IO读写异常导致进程使用内存大幅增加。
Linux内存问题排查全解析
169万+

被折叠的 条评论
为什么被折叠?



