如何衡量应用程序或者进程的实际内存使用情况?

时间:2020-03-06 14:42:08  来源:igfitidea点击:

这个问题在这里详细介绍。

我们如何衡量Linux中应用程序或者进程的内存使用情况?

从了解Linux上的内存使用的博客文章中,`ps'并不是用于此目的的准确工具。

Why ps is "wrong"
  
  Depending on how you look at it, ps is not reporting the real memory usage of processes. What it is really doing is showing how much real memory each process would take up if it were the only process running. Of course, a typical Linux machine has several dozen processes running at any given time, which means that the VSZ and RSS numbers reported by ps are almost definitely wrong.

解决方案

使用ps或者类似的工具,我们将只获得该进程分配的内存页面数量。这个数字是正确的,但是:

  • 不反映应用程序实际使用的内存量,仅反映为其保留的内存量
  • 如果页面被共享(例如由多个线程共享或者通过使用动态链接的库)可能会产生误导

如果我们真的想知道应用程序实际使用了多少内存,则需要在分析器中运行它。例如," valgrind"可以为我们提供有关使用的内存量的信息,更重要的是,我们可以了解程序中可能的内存泄漏。 valgrind的堆分析器工具称为" massif":

Massif is a heap profiler. It performs detailed heap profiling by taking regular snapshots of a program's heap. It produces a graph showing heap usage over time, including information about which parts of the program are responsible for the most memory allocations. The graph is supplemented by a text or HTML file that includes more information for determining where the most memory is being allocated. Massif runs programs about 20x slower than normal.

如valgrind文档中所述,我们需要通过valgrind运行程序:

valgrind --tool=massif <executable> <arguments>

Massif写入内存使用情况快照的转储(例如" massif.out.12345")。这些提供了(1)内存使用的时间表,(2)每个快照,记录了程序存储器中分配位置的记录。
massif-visualizer是分析这些文件的出色图形工具。但是我发现valgrind附带的一个简单的基于文本的工具ms_print已经很有帮助。

要查找内存泄漏,请使用valgrind的(默认)" memcheck"工具。

很难确定,但是这里有两个可以帮助的"接近"的事情。

$ ps aux

将为我们提供虚拟尺寸(VSZ)

我们还可以通过转到/ proc / $ pid / status从/ proc文件系统获取详细的统计信息。

最重要的是VmSize,它应该接近" ps aux"给出的值。

/proc/19420$ cat status
Name:   firefox
State:  S (sleeping)
Tgid:   19420
Pid:    19420
PPid:   1
TracerPid:  0
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
FDSize: 256
Groups: 4 6 20 24 25 29 30 44 46 107 109 115 124 1000 
VmPeak:   222956 kB
VmSize:   212520 kB
VmLck:         0 kB
VmHWM:    127912 kB
VmRSS:    118768 kB
VmData:   170180 kB
VmStk:       228 kB
VmExe:        28 kB
VmLib:     35424 kB
VmPTE:       184 kB
Threads:    8
SigQ:   0/16382
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000020001000
SigCgt: 000000018000442f
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
Cpus_allowed:   03
Mems_allowed:   1
voluntary_ctxt_switches:    63422
nonvoluntary_ctxt_switches: 7171

获取valgrind。给它运行程序,它将告诉我们很多有关它的内存使用情况。

这仅适用于程序运行一段时间并停止的情况。我不知道valgrind是否可以掌握已经在运行的进程,还是不应该停止诸如守护程序之类的进程。

没有简单的方法可以计算出来。但是有些人试图获得一些好的答案:

  • ps_mem.py
  • github上的ps_mem.py

这里是valgrind的另一票,但我想补充一点,我们可以使用Alleyoop之类的工具来解释valgrind生成的结果。

我一直使用这两个工具,并且总是有精益,无泄漏的代码来自豪地展示它;)

这个问题没有一个答案,因为我们无法精确指出一个进程使用的内存量。 Linux下的大多数进程都使用共享库。例如,假设我们要计算" ls"进程的内存使用情况。我们是否仅计算可执行文件" ls"使用的内存(如果可以隔离的话)? libc呢?还是运行" ls"所需的所有其他这些库?

linux-gate.so.1 =>  (0x00ccb000)
librt.so.1 => /lib/librt.so.1 (0x06bc7000)
libacl.so.1 => /lib/libacl.so.1 (0x00230000)
libselinux.so.1 => /lib/libselinux.so.1 (0x00162000)
libc.so.6 => /lib/libc.so.6 (0x00b40000)
libpthread.so.0 => /lib/libpthread.so.0 (0x00cb4000)
/lib/ld-linux.so.2 (0x00b1d000)
libattr.so.1 => /lib/libattr.so.1 (0x00229000)
libdl.so.2 => /lib/libdl.so.2 (0x00cae000)
libsepol.so.1 => /lib/libsepol.so.1 (0x0011a000)

我们可能会争辩说它们由其他进程共享,但是如果不加载" ls",则无法在系统上运行它们。

另外,如果我们需要知道一个进程需要多少内存才能进行容量规划,则必须计算该进程的每个其他副本使用多少内存。我认为/ proc / PID / status可能一次就给我们足够的内存使用信息。另一方面,valgrind将在程序的整个生命周期内为我们提供更好的内存使用情况配置文件

如果我们有时间运行,Valgrind会很棒。 valgrind --tool = massif是正确的解决方案。

但是,我开始运行更大的示例,并且使用valgrind不再可行。有没有办法确定程序的最大内存使用量(模数页面大小和共享页面)?

在真实的Unix系统上,/ usr / bin / time -v可以工作。但是,在Linux上,这不起作用。

在最新版本的linux中,使用smaps子系统。例如,对于PID为1234的进程:

cat /proc/1234/smaps

它会准确告诉我们当时它正在使用多少内存。更重要的是,它将内存分为私有内存和共享内存,因此我们可以知道程序实例正在使用多少内存,而不必包括程序多个实例之间共享的内存。

如果我们想要比使用Valgrind进行性能分析更快的速度并且内核更旧并且不能使用smaps,则带有选项以显示进程驻留集的ps(带有ps -o rss,command)可以为我们提供快速而合理地使用正在使用的非交换内存的实际数量的" aproximation"。

这是有关工具和问题的出色摘要:archive.org链接

我会引用它,以便更多的开发人员实际阅读它。

If you want to analyse memory usage of the whole system or to thoroughly analyse memory usage of one application (not just its heap usage), use exmap. For whole system analysis, find processes with the highest effective usage, they take the most memory in practice, find processes with the highest writable usage, they create the most data (and therefore possibly leak or are very ineffective in their data usage). Select such application and analyse its mappings in the second listview. See exmap section for more details. Also use xrestop to check high usage of X resources, especially if the process of the X server takes a lot of memory. See xrestop section for details.
  
  If you want to detect leaks, use valgrind or possibly kmtrace.
  
  If you want to analyse heap (malloc etc.) usage of an application, either run it in memprof or with kmtrace, profile the application and search the function call tree for biggest allocations. See their sections for more details.

Valgrind可以显示详细信息,但会大大降低目标应用程序的速度,并且在大多数情况下会更改应用程序的行为。
Exmap是我还不知道的东西,但是似乎我们需要一个内核模块来获取信息,这可能是一个障碍。

我假设每个人都想知道WRT的"内存使用情况"如下:
在linux中,单个进程可能使用的物理内存量可以大致分为以下几类。

  • .c clean == malloc /映射的堆和堆栈内存一旦分配,写入,然后释放,但尚未回收
  • .c clean == malloc / mmaped堆可以写时复制并在进程之间共享(已编辑)
  • .c clean ==映射的程序/库文本专用映射
  • .c clean ==映射库文本共享映射

Android中包含的名为showmap的实用程序非常有用

virtual                    shared   shared   private  private
size     RSS      PSS      clean    dirty    clean    dirty    object
-------- -------- -------- -------- -------- -------- -------- ------------------------------
       4        0        0        0        0        0        0 0:00 0                  [vsyscall]
       4        4        0        4        0        0        0                         [vdso]
      88       28       28        0        0        4       24                         [stack]
      12       12       12        0        0        0       12 7909                    /lib/ld-2.11.1.so
      12        4        4        0        0        0        4 89529                   /usr/lib/locale/en_US.utf8/LC_IDENTIFICATION
      28        0        0        0        0        0        0 86661                   /usr/lib/gconv/gconv-modules.cache
       4        0        0        0        0        0        0 87660                   /usr/lib/locale/en_US.utf8/LC_MEASUREMENT
       4        0        0        0        0        0        0 89528                   /usr/lib/locale/en_US.utf8/LC_TELEPHONE
       4        0        0        0        0        0        0 89527                   /usr/lib/locale/en_US.utf8/LC_ADDRESS
       4        0        0        0        0        0        0 87717                   /usr/lib/locale/en_US.utf8/LC_NAME
       4        0        0        0        0        0        0 87873                   /usr/lib/locale/en_US.utf8/LC_PAPER
       4        0        0        0        0        0        0 13879                   /usr/lib/locale/en_US.utf8/LC_MESSAGES/SYS_LC_MESSAGES
       4        0        0        0        0        0        0 89526                   /usr/lib/locale/en_US.utf8/LC_MONETARY
       4        0        0        0        0        0        0 89525                   /usr/lib/locale/en_US.utf8/LC_TIME
       4        0        0        0        0        0        0 11378                   /usr/lib/locale/en_US.utf8/LC_NUMERIC
    1156        8        8        0        0        4        4 11372                   /usr/lib/locale/en_US.utf8/LC_COLLATE
     252        0        0        0        0        0        0 11321                   /usr/lib/locale/en_US.utf8/LC_CTYPE
     128       52        1       52        0        0        0 7909                    /lib/ld-2.11.1.so
    2316       32       11       24        0        0        8 7986                    /lib/libncurses.so.5.7
    2064        8        4        4        0        0        4 7947                    /lib/libdl-2.11.1.so
    3596      472       46      440        0        4       28 7933                    /lib/libc-2.11.1.so
    2084        4        0        4        0        0        0 7995                    /lib/libnss_compat-2.11.1.so
    2152        4        0        4        0        0        0 7993                    /lib/libnsl-2.11.1.so
    2092        0        0        0        0        0        0 8009                    /lib/libnss_nis-2.11.1.so
    2100        0        0        0        0        0        0 7999                    /lib/libnss_files-2.11.1.so
    3752     2736     2736        0        0      864     1872                         [heap]
      24       24       24        0        0        0       24 [anon]
     916      616      131      584        0        0       32                         /bin/bash
-------- -------- -------- -------- -------- -------- -------- ------------------------------
   22816     4004     3005     1116        0      876     2012 TOTAL

尝试使用pmap命令:

sudo pmap -x <process pid>

如果代码是C或者C ++,则可以使用getrusage(),它返回有关进程的内存和时间使用情况的各种统计信息。

但是,并非所有平台都支持此功能,并且将为内存使用选项返回0值。

相反,我们可以查看在/ proc / [pid] / statm`中创建的虚拟文件(其中,[pid]被进程ID代替。我们可以从getpid()获取它)。

该文件看起来像是带有7个整数的文本文件。我们可能对该文件中的第一个(所有内存使用)和第六个(数据内存使用)数字最感兴趣。