确定进程“真实”内存使用情况的方法,即私人脏 RSS?

2024-10-29 08:35:00
admin
原创
48
摘要:问题描述:诸如“ps”和“top”之类的工具会报告各种内存使用情况,例如虚拟机大小和驻留集大小。但是,这些都不是“实际”内存使用情况:程序代码在同一程序的多个实例之间共享。共享库程序代码在使用该库的所有进程之间共享。一些应用程序分叉进程并与它们共享内存(例如通过共享内存段)。虚拟内存系统使得虚拟机大小报告几乎...

问题描述:

诸如“ps”和“top”之类的工具会报告各种内存使用情况,例如虚拟机大小和驻留集大小。但是,这些都不是“实际”内存使用情况:

  • 程序代码在同一程序的多个实例之间共享。

  • 共享库程序代码在使用该库的所有进程之间共享。

  • 一些应用程序分叉进程并与它们共享内存(例如通过共享内存段)。

  • 虚拟内存系统使得虚拟机大小报告几乎毫无用处。

  • 当一个进程被换出时,RSS 为 0,因此它不是很有用。

  • 等等等等。

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

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


解决方案 1:

在 OSX 上,活动监视器实际上会给你一个非常好的猜测。

私有内存肯定是仅由您的应用程序使用的内存。例如,堆栈内存和使用 malloc() 和类似函数/方法(Objective-C 的 alloc 方法)动态保留的所有内存都是私有内存。如果您分叉,私有内存将与您的子进程共享,但标记为写时复制。这意味着只要页面未被任何进程(父进程或子进程)修改,它就会在它们之间共享。只要任何一个进程修改任何页面,该页面就会在修改之前被复制。即使此内存与分叉子进程共享(并且只能分叉子进程共享),它仍显示为“私有”内存,因为在最坏的情况下,它的每一页都将被修改(迟早),然后它再次对每个进程都是私有的。

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

实际内存是当前“分配”给您的进程的 RAM 量,无论是私有内存还是共享内存。它可以是私有内存和共享内存的精确总和,但通常不是。分配给您的进程的内存可能比它当前需要的内存多(这会加快将来对更多内存的请求),但这对系统没有任何损失。如果另一个进程需要内存并且没有可用的空闲内存,则在系统开始交换之前,它会从您的进程中拿走多余的内存并将其分配给另一个进程(这是一个快速而轻松的操作);因此您的下一个 malloc 调用可能会稍微慢一些。实际内存也可以小于私有内存和物理内存;这是因为如果您的进程从系统请求内存,它将只收到“虚拟内存”。只要您不使用虚拟内存,它就不会链接到任何实际内存页面(因此 malloc 10 MB 内存,只使用其中一个字节,您的进程将只获得一个页面,4096 字节的内存分配 - 其余的内存仅在您真正需要时才分配)。交换的进一步内存可能也不计入实际内存(对此不确定),但它将计入共享和私有内存。

虚拟内存是应用程序进程空间中所有有效地址块的总和。这些地址可能链接到物理内存(同样是私有或共享的),也可能不链接到物理内存,但在这种情况下,只要您使用该地址,它们就会链接到物理内存。访问已知地址之外的内存地址将导致 SIGBUS 并且应用程序将崩溃。交换内存时,此内存的虚拟地址空间保持有效,访问这些地址会导致内存被换回。

结论:

如果您的应用未明确或隐式使用共享内存,则私有内存就是您的应用所需的内存量,这是由于堆栈大小(如果是多线程,则为大小)以及您对动态内存进行的 malloc() 调用。在这种情况下,您不必太在意共享内存或实际内存。

如果您的应用使用共享内存,其中包括图形 UI,其中内存在您的应用和 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 = Core Graphics,这些很可能是窗口内容、按钮、图像和其他缓存以进行快速绘制的数据。它已通过 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% 的内存已换出到磁盘)。

原因很简单:映射文件中的只读内存不会被交换。如果系统需要内存,则当前页面会从进程中删除,因为内存已经“交换”了。它仅由直接从文件映射的内容组成,并且可以在需要时重新映射此内容,因为文件仍在那里。这样,这块内存也不会浪费交换文件中的空间。只有可写内存必须先交换到文件,然后才能删除,因为其内容之前未存储在磁盘上。

解决方案 2:

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

解决方案 3:

你确实不能。

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

我对 RSS 很满意。但我知道你不能完全依赖它...

解决方案 4:

Top 知道如何做到这一点。它在 Debian Linux 上默认显示 VIRT、RES 和 SHR。VIRT = SWAP + RES。RES = CODE + DATA。SHR 是可能与另一个进程共享的内存(共享库或其他内存)。

此外,“脏”内存仅仅是已使用过和/或尚未交换的 RES 内存。

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

解决方案 5:

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

http://www.selenic.com/smem/

解决方案 6:

您可以从 /proc/pid/smaps 获取私人脏 RSS 和私人干净 RSS

解决方案 7:

重新设计这个以使其更加清晰,以展示 bash 中的一些适当的最佳实践,特别是使用awk而不是bc

find /proc/ -maxdepth 1 -name '[0-9]*' -print0 | while read -r -d $'' 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
", '"${pidpath##*/}"', pd)}' "${pidpath}/smaps" || break
done

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

        56: 106496
         1: 147456
        55: 155648

解决方案 8:

使用 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>):

解决方案 9:

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

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

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

至少在 FreeBSD 7.0 中,默认没有进行挂载(7.0 是一个更老的版本,但是对于这种基本问题,答案隐藏在邮件列表中!)

解决方案 10:

检查一下,这是 gnome-system-monitor 的源代码,它认为一个进程“真正使用info->mem”的内存是X Server Memory( info->memxserver) 与 Writable Memory( ) 的总和( “可写内存” info->memwritable),“可写内存”是/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);
}
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用