问与答 linux-一种确定进程的“实际”内存使用情况的方法,即私有脏RSS?

baker · 2020-03-15 17:00:18 · 热度: 24

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

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

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

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

猜你喜欢:
共收到 10 条回复
alton #1 · 2020-03-15 17:00:19

在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%的内存已换出到磁盘)

muriel #2 · 2020-03-15 17:00:20

在Linux上,您可能需要/ proc / self / smaps中的PSS(比例集大小)数字。 映射的PSS是其RSS除以使用该映射的进程数。

tyshawn #3 · 2020-03-15 17:00:21

Top知道如何执行此操作。 它在Debian Linux上默认显示VIRT,RES和SHR。 VIRT =交换+ RES。 RES =代码+数据。 SHR是可以与另一个进程共享的内存(共享库或其他内存。)

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

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

raphael #4 · 2020-03-15 17:00:23

你真的不行

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

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

sydney #5 · 2020-03-15 17:00:24

您可以从/ proc / pid / smaps获得私有的脏的和私有的干净的RSS

sunil #6 · 2020-03-15 17:00:25

看看smem。 它将为您提供PSS信息

[HTTP://呜呜呜.塞勒昵称.com/什么吗/]

les #7 · 2020-03-15 17:00:27

使用mincore(2)系统调用。 引用手册页:

DESCRIPTION
     The mincore() system call determines whether each of the pages in the
     region beginning at addr and continuing for len bytes is resident.  The
     status is returned in the vec array, one character per page.  Each
     character is either 0 if the page is not resident, or a combination of
     the following flags (defined in <sys/mman.h>):
skip #8 · 2020-03-15 17:00:28

对其进行了重新设计,使其更加简洁,以演示一些正确的bash最佳实践,尤其是使用| sort -n -k 2代替bc

find /proc/ -maxdepth 1 -name '[0-9]*' -print0 | while read -r -d $'\0' pidpath; do
  [ -f "${pidpath}/smaps" ] || continue
  awk '!/^Private_Dirty:/ {next;}
       $3=="kB" {pd += $2 * (1024^1); next}
       $3=="mB" {pd += $2 * (1024^2); next}
       $3=="gB" {pd += $2 * (1024^3); next}
       $3=="tB" {pd += $2 * (1024^4); next}
       $3=="pB" {pd += $2 * (1024^5); next}
       {print "ERROR!!  "$0 >"/dev/stderr"; exit(1)}
       END {printf("%10d: %d\n", '"${pidpath##*/}"', pd)}' "${pidpath}/smaps" || break
done

在我机器上的一个方便的小容器上,用| sort -n -k 2对输出进行排序,如下所示:

        56: 106496
         1: 147456
        55: 155648
triston #9 · 2020-03-15 17:00:29

对于提到Freebsd的问题,感到惊讶的是没有人写过这个:

如果您想要Linux风格的/ proc / PROCESSID / status输出,请执行以下操作:

mount -t linprocfs none /proc
cat /proc/PROCESSID/status

在FreeBSD 7.0中,Atleast默认情况下未完成安装(7.0是一个较旧的发行版,但是对于此基本功能,答案已隐藏在邮件列表中!)

mortimer #10 · 2020-03-15 17:00:31

看看,这是gnome-system-monitor的源代码,它认为一个进程“真正使用”的内存是X服务器内存(info->memxserver)和可写内存(2755668236090990999810)的总和(info->mem)和“可写内存” “是在/ proc / PID / smaps文件中标记为“ Private_Dirty”的内存块。

除了linux系统以外,根据gnome-system-monitor代码可能是不同的方式。

static void
get_process_memory_writable (ProcInfo *info)
{
    glibtop_proc_map buf;
    glibtop_map_entry *maps;

    maps = glibtop_get_proc_map(&buf, info->pid);

    gulong memwritable = 0;
    const unsigned number = buf.number;

    for (unsigned i = 0; i < number; ++i) {
#ifdef __linux__
        memwritable += maps[i].private_dirty;
#else
        if (maps[i].perm & GLIBTOP_MAP_PERM_WRITE)
            memwritable += maps[i].size;
#endif
    }

    info->memwritable = memwritable;

    g_free(maps);
}

static void
get_process_memory_info (ProcInfo *info)
{
    glibtop_proc_mem procmem;
    WnckResourceUsage xresources;

    wnck_pid_read_resource_usage (gdk_screen_get_display (gdk_screen_get_default ()),
                                  info->pid,
                                  &xresources);

    glibtop_get_proc_mem(&procmem, info->pid);

    info->vmsize    = procmem.vsize;
    info->memres    = procmem.resident;
    info->memshared = procmem.share;

    info->memxserver = xresources.total_bytes_estimate;

    get_process_memory_writable(info);

    // fake the smart memory column if writable is not available
    info->mem = info->memxserver + (info->memwritable ? info->memwritable : info->memres);
}
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册