Linux过程的峰值内存使用
1. 概述
Linux 的设计旨在尽可能高效地使用所有可用的物理内存。但是,有时,系统资源限制会导致服务器端的突然行为。通常,这些限制会触发高 CPU 和高内存使用率。无论如何,我们可以通过观察进程的内存利用率来真正避免这种突然的行为。
在本教程中,我们将学习使用一些著名的 Linux 命令来实现一些技巧和窍门,以帮助我们识别进程的内存使用峰值。
2. 监控内存的传统命令
在大多数情况下,top */ htop / atop *等命令为我们提供了流程概览。在特定情况下,它们也可用于监视特定过程。在这里,我们专注于检查进程以确定其峰值内存利用率。
我们可以从调查顶部 结果开始,以查看过程概述。这给出了一个关于所有进程都在使用什么的想法。
假设我们需要对特定进程进行归零,并且我们知道它的进程 ID (PID):
$ top -p 7
top - 10:25:53 up 19 min, 0 users, load average: 0.52, 0.58, 0.59
Tasks: 1 total, 0 running, 1 sleeping, 0 stopped, 0 zombie
%Cpu(s): 16.0 us, 6.2 sy, 0.0 ni, 77.1 id, 0.0 wa, 0.7 hi, 0.0 si, 0.0 st
MiB Mem : 3961.1 total, 665.1 free, 3072.0 used, 224.0 buff/cache
MiB Swap: 12288.0 total, 11806.3 free, 481.7 used. 758.5 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
7 user1 20 0 18076 3632 3528 S 0.0 0.1 0:00.19 bash
现在我们来看看bash的用法:
$ top | grep bash
7 user1 20 0 18076 3632 3528 S 0.0 0.1 0:00.19 bash
73 root 20 0 18076 3604 3540 S 0.0 0.1 0:00.17 bash
htop与*top 类似,*但显示更多关于进程的数据。有命令栏,方便识别进程路径。
atop又是一个类似top和htop的命令。它的优点是在文件中记录输出的非常有用的功能。
考虑一个在特定时间窗口一次又一次发生的问题。为了保持记录,我们可以安排一个 cron 作业将输出写入文件,这样以后回放就变得可行了。如果我们需要将输出记录到文件中,我们将使用atop -w :
$ atop -w filename
如果我们需要播放该文件的输出,我们将使用atop -r :
$ atop -r filename
PID SYSCPU USRCPU VGROW RGROW RUID EUID ST EXC THR S CPUNR CPU CMD 1/1 73 0.17s 0.03s 481.1G 3604K root root N- - 1 S 0 0% bash
7 0.10s 0.09s 591.0G 3632K user1 user1 N- - 1 S 0 0% bash
1 0.15s 0.00s 376.9G 316K root root N- - 2 S 0 0% init
71 0.04s 0.00s 716.4G 2908K root root N- - 1 S 0 0% sudo
72 0.03s 0.00s 820.0G 2092K root root N- - 1 S 0 0% su
109 0.01s 0.00s 1.0T 2136K root root N- - 1 R 0 0% atop
6 0.00s 0.01s 376.9G 224K root root N- - 1 S 0 0% init
这三个命令可以成为持续调查的最佳工具。对于我们的情况,我们可以在进程达到设定的限制之前识别出进程的峰值内存使用量。
3. grep 单行
/proc 虚拟文件系统 是一个目录,其中包含代表 Linux 内核当前状态的文件层次结构。它还包括有关任何当前正在运行的进程的信息。
这是一个确定具有进程 id (PID) 113 的此类进程的峰值内存使用量的单行程序:
$ grep ^VmPeak /proc/113/status
VmPeak: 2252 kB
我们还可以查找“VmHWM:Peak resident set size”来衡量 RAM 使用情况。VmPeak 是最大总内存使用量,包括虚拟内存,而 VmHWM 是峰值 RAM 使用量。
众所周知,/proc是一个虚拟文件系统,因此从它的文件中读取与从普通文件系统中读取是不同的。从/proc中删除有关进程的信息比从真正的文件系统中删除要快得多(此处涉及脏缓存刷新 )。
考虑到这一点,假设我们需要读取尚未缓冲的进程的下一行。在这种情况下,有关它的信息可能已被删除。我们可能不需要有关不再存在的流程的信息。解决方案是要么考虑文件丢失,要么缓冲整个文件,然后解析它。
4. GNU时间
让我们通过几个例子来了解给定上下文中的 GNU*time *。 假设我们想知道“ top ”进程的内存使用峰值。在这种情况下,“最大驻留集大小”告诉我们:
$ /usr/bin/time -v top | grep "Maximum resident set size"
Maximum resident set size (kbytes): 2252
GNU时间支持格式选项。除了内存使用之外,带有*%P选项的 GNU**时间*提供了不相关的统计信息 (%CPU),这取决于调度程序,因此变化很大:
$ /usr/bin/time -f "%P %M" top
2% 2248
在 bash 中,我们需要指定完整路径,例如/usr/bin/time,因为 bash 内置的time关键字不支持*-f*选项**:
$ /usr/bin/time -f '%M' top
2248
我们还可以创建别名或调整环境以使用 GNU时间来获取平均和最大内存信息:
alias time="$(which time) -f '\t%E real,\t%U user,\t%S sys,\t%K amem,\t%M mmem'"
export TIME="$(which time) -f '\t%E real,\t%U user,\t%S sys,\t%K amem,\t%M mmem'"
存储在内存段 中的这些信息代表了进程的平均总(数据+堆栈+文本)内存使用(K)和最大驻留集大小(M)。
4.1. 论据和注意事项
让我们通过讨论一些已知问题来进行time命令事实检查。
第一个问题是某些 Linux 系统上的内存报告可能会浪费时间。**当我们使用/usr/bin/time -v ls确定“最大驻留集大小”时,我们可以看到大多数时候它返回 0。它总是返回 0,因为时间从wait3(2) 系统中获取大部分信息称呼。
没有*wait3(2)调用的系统,使用time(2) 系统调用。但是,它提供的信息比wait3(2)*少得多。因此,此类系统将大部分资源报告为 0。
或者,尝试更占用 CPU 的命令可能会奏效。
第二个问题是在调用time -v时,输出**“bash: -v: command not found”*意味着 bash 拦截时间以使用自己的内置时间函数。使用/bin/time -v*解决了这个问题。
第三,GNU time有一个 bug。它报告实际内存使用量的 4 倍。Ubuntu 14.4 上的time 1.7-24和 Fedora 1.7-3 版本的time包包含对内存报告的修复。
4.2. 内置time与 GNUtime
如上所述,在 bash 中,我们需要指定 GNU time的完整路径,例如*/usr/bin/time*。或者,我们可以使用命令 time -l。
请务必注意,该命令不是占位符。命令 time与time不同。命令 time -l调用 shell 来调用名为time的二进制文件,而不是内置函数。
5. Valgrind 单线
*Valgrind *是一个为用户空间二进制文件提供检测的框架。它附带了几个可用于分析和分析程序性能的工具。
Massif 是Valgrind工具之一,用于测量特定程序使用的堆内存。一个简单的valgrind massif one-liner 来描述“ top ”进程的峰值内存使用情况,如下所示:
$ valgrind --tool=massif --pages-as-heap=yes --massif-out-file=massif.out top; grep mem_heap_B massif.out | sed -e 's/mem_heap_B=\(.*\)/\1/' | sort -g | tail -n 1
==746== Massif, a heap profiler
==746== Copyright (C) 2003-2017, and GNU GPL'd, by Nicholas Nethercote
==746== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==746== Command: top
==746==
==746== error calling PR_SET_PTRACER, vgdb might block
top - 21:47:03 up 23 min, 0 users, load average: 0.52, 0.58, 0.59
Tasks: 8 total, 1 running, 6 sleeping, 1 stopped, 0 zombie
%Cpu(s): 2.8 us, 1.9 sy, 0.0 ni, 95.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
MiB Mem : 3961.1 total, 408.3 free, 3328.8 used, 224.0 buff/cache
MiB Swap: 12288.0 total, 12014.8 free, 273.2 used. 501.7 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 8940 308 268 S 0.0 0.0 0:00.20 init
6 root 20 0 8940 220 180 S 0.0 0.0 0:00.01 init
7 user1 20 0 18080 3456 3348 S 0.0 0.1 0:00.27 bash
72 root 20 0 18924 2900 2800 S 0.0 0.1 0:00.10 sudo
73 root 20 0 18048 2080 2056 S 0.0 0.1 0:00.07 su
74 root 20 0 18076 3608 3516 S 0.0 0.1 0:00.41 bash
366 root 20 0 18444 1784 1324 T 0.0 0.0 0:00.01 top
746 root 20 0 70528 26284 3072 R 0.0 0.6 0:01.24 massif-amd64-li
==746==
15691776
输出 15691776 是“ top ”进程的内存使用峰值。
默认情况下,Massif 仅测量堆内存。但是,如果我们希望测量程序使用的所有内存,我们可以使用 — pages-as-heap=yes 。
Massif将分析数据输出到massif.out文件。ms_print 工具将此分析数据绘制成图表,以显示程序执行过程中的内存消耗。它还显示了有关在内存分配峰值点负责分配的站点的详细信息。我们可以使用以下命令绘制 massif.out文件中的数据:
ms_print massif.out
6. 其他现代工具:Heaptrack 和 Busybox
*Heaptrack *是一个同时具有 GUI 和文本界面的 KDE 工具。它以火焰图的形式提供峰值内存使用量。它比 Valgrind 做的检查更少,因此速度更快。
我们可以通过跟踪*/proc/[pid]/smaps中的 PSS 数量或使用pmap来确定峰值内存。我们还可以将heaptrack*附加到已经运行的进程:
heaptrack --pid $(pid of <your application>)
heaptrack输出被写入*/tmp/heaptrack.APP.PID.gz*。
*Busybox *将许多常见 UNIX 实用程序的微小版本组合成一个小型可执行文件。它为我们通常在 GNU Coreutils、until-Linux 等中找到的大多数实用程序提供了极简主义的替代品。
Busybox预装在大多数 Linux 发行版中,包括 Debian 和 Ubuntu。它可以用来代替许多现代发行版中不存在的命令。同样,要了解峰值内存使用情况,我们可以使用带有*-v参数的busybox 时间*实现:
$ /usr/bin/time busybox time -v uname -r | grep "Maximum resident set size"
Maximum resident set size (kbytes): 1792
它的输出类似于 GNU时间输出。