确定进程的"实际"内存使用情况的方法,即私有脏RSS?

时间:2020-03-06 14:34:21  来源:igfitidea点击:

诸如" ps"和" top"之类的工具报告各种内存使用情况,例如VM大小和Resident Set Size。但是,这些都不是"实际"的内存使用情况:

  • 程序代码在同一程序的多个实例之间共享。
  • 共享库程序代码在使用该库的所有进程之间共享。
  • 某些应用会分派进程并与其共享内存(例如,通过共享内存段)。
  • 虚拟内存系统使VM大小报告几乎毫无用处。
  • 换出一个进程时,RSS为0,因此它不是很有用。
  • 等等

我发现,Linux报告的私有脏RSS是最接近"实际"内存使用情况的东西。这可以通过将" / proc / somepid / smaps"中的所有" Private_Dirty"值相加来获得。

但是,其他操作系统是否提供类似的功能?如果没有,有哪些替代方案?我特别对FreeBSD和OS X感兴趣。

解决方案

顶知道如何做到这一点。它在Debian Linux上默认显示VIRT,RES和SHR。 VIRT =交换+ RES。 RES =代码+数据。 SHR是可以与另一个进程共享的内存(共享库或者其他内存。)

同样,"脏"存储器仅是已使用和/或者尚未交换的RES存储器。

很难说,但是最好的理解方法是看一个不交换的系统。然后,RES SHR是进程专用内存。但是,这不是一个好方法,因为我们不知道SHR中的内存正在被另一个进程使用。它可能代表仅由进程使用的未编写共享对象页面。

你真的不行

我的意思是,进程之间的共享内存...我们是否要计算它。如果不计算,那就错了。所有进程的内存使用量之和不会成为总内存使用量。如果我们数数,我们将要数两次,这将是不正确的。

我,我对RSS感到满意。而且知道我们不能真正完全依靠它...

在OSX上,活动监视器实际上为我们提供了一个很好的猜测。

专用内存可以确保仅由应用程序使用的内存。例如。堆栈内存和使用malloc()和类似功能/方法(Objective-C的alloc方法)动态保留的所有内存均为私有内存。如果我们分叉,则私有内存将与孩子共享,但标记为写时复制。这意味着只要页面没有被任何进程(父进程或者子进程)修改,它们就可以在它们之间共享。一旦任一进程修改了任何页面,该页面就会在被修改之前被复制。即使此内存与fork子代共享(并且只能与fork子代共享),它仍显示为"私有"内存,因为在最坏的情况下,它的每一页都会被修改(早或者晚),并且然后它又对每个进程都是私有的。

共享内存既可以是当前共享的内存(在不同进程的虚拟进程空间中可以看到相同的页面),也可以是将来可能共享的内存(例如只读内存,因为没有理由不共享读取的内存) -仅内存)。至少这就是我从Apple那里读取某些命令行工具的源代码的方式。因此,如果我们在使用mmap的进程之间共享内存(或者将同一内存映射到多个进程的类似调用),则这将是共享内存。但是,可执行代码本身也是共享内存,因为如果启动了应用程序的另一个实例,则没有理由不共享已加载到内存中的代码(默认情况下,可执行代码页是只读的,除非我们正在运行调试器中的应用程序)。因此,共享内存实际上是应用程序使用的内存,就像私有内存一样,但是它可能另外与另一个进程共享(或者可能不是,但是,如果共享了,为什么它不计入应用程序?)

实内存是当前"分配"给进程的RAM数量,无论是私有的还是共享的。这可以恰好是私有和共享的总和,但通常不是。进程分配的内存可能会超过当前所需的内存(这会加快将来对更多内存的请求),但这不会给系统带来任何损失。如果另一个进程需要内存并且没有可用的内存,则在系统开始交换之前,它将多余的内存从进程中移走,并为其分配另一个进程(这是一种快速而轻松的操作);因此,下一个malloc调用可能会比较慢。实际内存也可以小于私有内存和物理内存。这是因为如果进程从系统请求内存,它将仅接收"虚拟内存"。只要我们不使用此虚拟内存,它就不会链接到任何实际内存页面(因此malloc 10 MB内存,仅使用一个字节,进程将只分配一个页面,即4096字节的内存)其余的仅在我们确实需要时才分配)。交换的其他内存也可能不会计入实际内存(对此不确定),但是会计入共享内存和私有内存。

虚拟内存是在应用进程空间中被认为有效的所有地址块的总和。这些地址可能链接到物理内存(又是私有的或者共享的),也可能没有,但是在这种情况下,一旦我们使用该地址,它们就会链接到物理内存。访问已知地址之外的内存地址将导致SIGBUS,并且应用程序将崩溃。交换内存后,该内存的虚拟地址空间仍然有效,访问这些地址会导致内存被交换回去。

结论:
如果应用程序没有显式或者隐式使用共享内存,则由于堆栈大小(或者多线程大小)以及由于我们对动态内存进行的malloc()调用,专用内存是应用程序需要的内存量。在这种情况下,我们不必太在意共享或者实际内存。

如果应用程序使用共享内存,并且其中包括一个图形用户界面,例如在应用程序和WindowServer之间共享内存,那么我们也可以查看共享内存。很高的共享内存数量可能意味着我们当前在内存中加载了太多的图形资源。

实际内存对于应用程序开发几乎没有兴趣。如果它大于共享和私有总和,则意味着系统懒于从进程中占用内存。如果较小,则进程所请求的内存超过了实际需要的内存,这也不错,因为只要我们不使用所有请求的内存,就不会从系统"窃取"内存。如果它比共享和私有的总和小得多,则我们可能只考虑在可能的情况下请求较少的内存,因为我们有点超额使用了内存(同样,这还不错,但这告诉我代码不是已针对内存使用量进行了最小化优化,并且如果它是跨平台的,则其他平台可能没有如此复杂的内存处理,因此我们可能更喜欢分配许多小块而不是几个大块,或者更快地释放内存,等等。在)。

如果我们对所有这些信息仍然不满意,则可以获得更多信息。打开一个终端并运行:

sudo vmmap <pid>

流程的流程ID在哪里。这将为我们显示进程空间中每个内存块的统计信息,包括开始和结束地址。它还会告诉我们该内存的来源(映射文件,堆栈内存或者Malloc的内存?可执行文件的__DATA或者__TEXT部分?),大小(KB),访问权限以及是否为私有,共享或者写时复制。如果是从文件映射的,它甚至会为我们提供文件的路径。

如果只希望"实际" RAM使用,请使用

sudo vmmap -resident <pid>

现在,它将为每个内存块显示虚拟内存块的大小以及当前物理内存中实际存在的内存块数量。

每个转储末尾还有一个概览表,其中包含不同内存类型的总和。该表目前在我的系统上适用于Firefox:

REGION TYPE             [ VIRTUAL/RESIDENT]
===========             [ =======/========]
ATS (font support)      [   33.8M/   2496K]
CG backing stores       [   5588K/   5460K]
CG image                [     20K/     20K]
CG raster data          [    576K/    576K]
CG shared images        [   2572K/   2404K]
Carbon                  [   1516K/   1516K]
CoreGraphics            [      8K/      8K]
IOKit                   [  256.0M/      0K]
MALLOC                  [  256.9M/  247.2M]
Memory tag=240          [      4K/      4K]
Memory tag=242          [     12K/     12K]
Memory tag=243          [      8K/      8K]
Memory tag=249          [    156K/     76K]
STACK GUARD             [  101.2M/   9908K]
Stack                   [   14.0M/    248K]
VM_ALLOCATE             [   25.9M/   25.6M]
__DATA                  [   6752K/   3808K]
__DATA/__OBJC           [     28K/     28K]
__IMAGE                 [   1240K/    112K]
__IMPORT                [    104K/    104K]
__LINKEDIT              [   30.7M/   3184K]
__OBJC                  [   1388K/   1336K]
__OBJC/__DATA           [     72K/     72K]
__PAGEZERO              [      4K/      0K]
__TEXT                  [  108.6M/   63.5M]
__UNICODE               [    536K/    512K]
mapped file             [  118.8M/   50.8M]
shared memory           [    300K/    276K]
shared pmap             [   6396K/   3120K]

这告诉我们什么?例如。 Firefox二进制文件及其加载的所有库的__TEXT部分中总共有108 MB数据,但目前其中只有63 MB驻留在内存中。字体支持(ATS)需要33 MB,但实际上只有约2.5 MB的内存。它使用了超过5 MB的CG后备存储,CG =核心图形,这很可能是窗口内容,按钮,图像和其他数据,这些数据已缓存以便快速绘制。它已通过malloc调用请求了256 MB,当前247 MB​​实际上已映射到内存页面。它为堆栈保留了14 MB的空间,但现在实际上仅使用248 KB的堆栈空间。

vmmap在表格上方也有一个很好的摘要

ReadOnly portion of Libraries: Total=139.3M resident=66.6M(48%) swapped_out_or_unallocated=72.7M(52%)
Writable regions: Total=595.4M written=201.8M(34%) resident=283.1M(48%) swapped_out=0K(0%) unallocated=312.3M(52%)

这显示了OS X的一个有趣的方面:​​对于只读内存,如果换出内存或者只是简单地未分配内存,它将不起作用。只有居民而没有居民。对于可写内存,这有所作为(在我的情况下,从未使用过所有请求的内存的52%,并且这种内存未分配,因此0%的内存已换出到磁盘)