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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/118307/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 16:30:06  来源:igfitidea点击:

A way to determine a process's "real" memory usage, i.e. private dirty RSS?

linuxmacosmemorymemory-managementfreebsd

提问by Hongli

Tools like 'ps' and 'top' report various kinds of memory usages, such as the VM size and the Resident Set Size. However, none of those are the "real" memory usage:

“ps”和“top”等工具报告各种内存使用情况,例如 VM 大小和驻留集大小。但是,这些都不是“真正的”内存使用情况:

  • Program code is shared between multiple instances of the same program.
  • Shared library program code is shared between all processes that use that library.
  • Some apps fork off processes and share memory with them (e.g. via shared memory segments).
  • The virtual memory system makes the VM size report pretty much useless.
  • RSS is 0 when a process is swapped out, making it not very useful.
  • Etc etc.
  • 程序代码在同一程序的多个实例之间共享。
  • 共享库程序代码在使用该库的所有进程之间共享。
  • 一些应用程序分叉进程并与它们共享内存(例如,通过共享内存段)。
  • 虚拟内存系统使 VM 大小报告几乎毫无用处。
  • 当一个进程被换出时,RSS 为 0,这使得它不是很有用。
  • 等等等等。

I've found that the private dirty RSS, as reported by Linux, is the closest thing to the "real" memory usage. This can be obtained by summing all Private_Dirtyvalues in /proc/somepid/smaps.

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

However, do other operating systems provide similar functionality? If not, what are the alternatives? In particular, I'm interested in FreeBSD and OS X.

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

回答by Chris

Top knows how to do this. It shows VIRT, RES and SHR by default on Debian Linux. VIRT = SWAP + RES. RES = CODE + DATA. SHR is the memory that may be shared with another process (shared library or other memory.)

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

Also, 'dirty' memory is merely RES memory that has been used, and/or has not been swapped.

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

It can be hard to tell, but the best way to understand is to look at a system that isn't swapping. Then, RES - SHR is the process exclusive memory. However, that's not a good way of looking at it, because you don't know that the memory in SHR is being used by another process. It may represent unwritten shared object pages that are only used by the process.

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

回答by alex

You really can't.

你真的不能。

I mean, shared memory between processes... are you going to count it, or not. If you don't count it, you are wrong; the sum of all processes' memory usage is not going to be the total memory usage. If you count it, you are going to count it twice- the sum's not going to be correct.

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

Me, I'm happy with RSS. And knowing you can't really rely on it completely...

我,我对 RSS 很满意。并且知道你不能真正完全依赖它......

回答by Mecki

On OSX the Activity Monitor gives you actually a very good guess.

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

Private memory is for sure memory that is only used by your application. E.g. stack memory and all memory dynamically reserved using malloc() and comparable functions/methods (alloc method for Objective-C) is private memory. If you fork, private memory will be shared with you child, but marked copy-on-write. That means as long as a page is not modified by either process (parent or child) it is shared between them. As soon as either process modifies any page, this page is copied before it is modified. Even while this memory is shared with fork children (and it can onlybe shared with fork children), it is still shown as "private" memory, because in the worst case, every page of it will get modified (sooner or later) and then it is again private to each process again.

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

Shared memory is either memory that is currently shared (the same pages are visible in the virtual process space of different processes) or that is likely to become shared in the future (e.g. read-only memory, since there is no reason for not sharing read-only memory). At least that's how I read the source code of some command line tools from Apple. So if you share memory between processes using mmap (or a comparable call that maps the same memory into multiple processes), this would be shared memory. However the executable code itself is also shared memory, since if another instance of your application is started there is no reason why it may not share the code already loaded in memory (executable code pages are read-only by default, unless you are running your app in a debugger). Thus shared memory is really memory used by your application, just like private one, but it might additionally be shared with another process (or it might not, but why would it not count towards your application if it was shared?)

共享内存要么是当前共享的内存(相同页面在不同进程的虚拟进程空间中可见),要么是将来可能共享的内存(例如只读内存,因为没有理由不共享读取- 唯一的记忆)。至少我是这样阅读 Apple 的一些命令行工具的源代码的。因此,如果您使用 mmap(或将同一内存映射到多个进程的类似调用)在进程之间共享内存,则这将是共享内存。然而,可执行代码本身也是共享内存,因为如果你的应用程序的另一个实例被启动,它没有理由不共享已经加载到内存中的代码(可执行代码页默认是只读的,除非你正在运行你的应用程序)调试器中的应用程序)。因此共享内存实际上是您的应用程序使用的内存,

Real memory is the amount of RAM currently "assigned" to your process, no matter if private or shared. This can be exactly the sum of private and shared, but usually it is not. Your process might have more memory assigned to it than it currently needs (this speeds up requests for more memory in the future), but that is no loss to the system. If another process needs memory and no free memory is available, before the system starts swapping, it will take that extra memory away from your process and assign it another process (which is a fast and painless operation); therefor your next malloc call might be somewhat slower. Real memory can also be smaller than private and physical memory; this is because if your process requests memory from the system, it will only receive "virtual memory". This virtual memory is not linked to any real memory pages as long as you don't use it (so malloc 10 MB of memory, use only one byte of it, your process will get only a single page, 4096 byte, of memory assigned - the rest is only assigned if you actually ever need it). Further memory that is swapped may not count towards real memory either (not sure about this), but it will count towards shared and private memory.

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

Virtual memory is the sum of all address blocks that are consider valid in your apps process space. These addresses might be linked to physical memory (that is again private or shared), or they might not, but in that case they will be linked to physical memory as soon as you use the address. Accessing memory addresses outside of the known addresses will cause a SIGBUS and your app will crash. When memory is swapped, the virtual address space for this memory remains valid and accessing those addresses causes memory to be swapped back in.

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

Conclusion:
If your app does not explicitly or implicitly use shared memory, private memory is the amount of memory your app needs because of the stack size (or sizes if multithreaded) and because of the malloc() calls you made for dynamic memory. You don't have to care a lot for shared or real memory in that case.

结论:
如果您的应用程序没有显式或隐式地使用共享内存,私有内存是您的应用程序需要的内存量,因为堆栈大小(或多线程时的大小)以及您为动态内存进行的 malloc() 调用。在这种情况下,您不必非常关心共享内存或真实内存。

If your app uses shared memory, and this includes a graphical UI, where memory is shared between your application and the WindowServer for example, then you might have a look at shared memory as well. A very high shared memory number may mean you have too many graphical resources loaded in memory at the moment.

如果您的应用程序使用共享内存,并且这包括图形 UI,例如,其中内存在您的应用程序和 WindowServer 之间共享,那么您也可以查看共享内存。非常高的共享内存数量可能意味着您目前在内存中加载了过多的图形资源。

Real memory is of little interest for app development. If it is bigger than the sum of shared and private, then this means nothing other than that the system is lazy at taken memory away from your process. If it is smaller, then your process has requested more memory than it actually needed, which is not bad either, since as long as you don't use all of the requested memory, you are not "stealing" memory from the system. If it is much smaller than the sum of shared and private, you may only consider to request less memory where possible, as you are a bit over-requesting memory (again, this is not bad, but it tells me that your code is not optimized for minimal memory usage and if it is cross platform, other platforms may not have such a sophisticated memory handling, so you may prefer to alloc many small blocks instead of a few big ones for example, or free memory a lot sooner, and so on).

应用程序开发对真实内存没什么兴趣。如果它大于共享和私有的总和,那么这仅意味着系统懒惰地从您的进程中取出内存。如果它更小,那么您的进程请求的内存比实际需要的多,这也不错,因为只要您不使用所有请求的内存,就不会从系统“窃取”内存。如果它远小于 shared 和 private 的总和,你可能只考虑在可能的情况下请求更少的内存,因为你有点过度请求内存(同样,这还不错,但它告诉我你的代码不是针对最小内存使用进行了优化,如果它是跨平台的,其他平台可能没有如此复杂的内存处理,

If you are still not happy with all that information, you can get even more information. Open a terminal and run:

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

sudo vmmap <pid>

where is the process ID of your process. This will show you statistics for EVERYblock of memory in your process space with start and end address. It will also tell you where this memory came from (A mapped file? Stack memory? Malloc'ed memory? A __DATA or __TEXT section of your executable?), how big it is in KB, the access rights and whether it is private, shared or copy-on-write. If it is mapped from a file, it will even give you the path to the file.

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

If you want only "actual" RAM usage, use

如果您只想要“实际”RAM 使用量,请使用

sudo vmmap -resident <pid>

Now it will show for every memory block how big the memory block is virtually and how much of it is really currently present in physical memory.

现在,它将为每个内存块显示内存块实际上有多大以及当前实际存在于物理内存中的内存块有多少。

At the end of each dump is also an overview table with the sums of different memory types. This table looks like this for Firefox right now on my system:

在每个转储的末尾还有一个概述表,其中包含不同内存类型的总和。这个表现在在我的系统上对于 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]

What does this tell us? E.g. the Firefox binary and all library it loads have 108 MB data together in their __TEXT sections, but currently only 63 MB of those are currently resident in memory. The font support (ATS) needs 33 MB, but only about 2.5 MB are really in memory. It uses a bit over 5 MB CG backing stores, CG = Core Graphics, those are most likely window contents, buttons, images and other data that is cached for fast drawing. It has requested 256 MB via malloc calls and currently 247 MB are really in mapped to memory pages. It has 14 MB space reserved for stacks, but only 248 KB stack space is really in use right now.

这告诉我们什么?例如,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 also has a good summary above the table

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%)

And this shows an interesting aspect of the OS X: For read only memory it plays no role if it is swapped out or simply unallocated; there is only resident and not resident. For writable memory this makes a difference (in my case 52% of all requested memory has never been used and is such unallocated, 0% of memory has been swapped out to disk)

这显示了 OS X 一个有趣的方面:​​对于只读内存,如果它被换出或只是未分配,它就不起作用;只有常驻,没有常驻。对于可写内存,这会有所不同(在我的情况下,所有请求的内存中有 52% 从未使用过并且未分配,0% 的内存已换出到磁盘)

回答by Justin L.

On Linux, you may want the PSS (proportional set size) numbers in /proc/self/smaps. A mapping's PSS is its RSS divided by the number of processes which are using that mapping.

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

回答by Ankur Agarwal

You can get private dirty and private clean RSS from /proc/pid/smaps

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

回答by Arvind

For a question that mentioned Freebsd, surprised no one wrote this yet :

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

If you want a linux style /proc/PROCESSID/status output, please do the following :

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

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

Atleast in FreeBSD 7.0, the mounting was not done by default ( 7.0 is a much older release,but for something this basic,the answer was hidden in a mailing list!)

至少在 FreeBSD 7.0 中,安装不是默认完成的(7.0 是一个更旧的版本,但对于这种基本的东西,答案隐藏在邮件列表中!)

回答by BMDan

Reworked this to be much cleaner, to demonstrate some proper best practices in bash, and in particular to use awkinstead of bc.

返工这是干净多了,证明在bash一些适当的最佳实践,特别是使用awk替代bc

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

On a handy little container on my machine, with | sort -n -k 2to sort the output, this looks like:

在我机器上的一个方便的小容器上,| sort -n -k 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>):

回答by workplaylifecycle

Check it out, this is the source code of gnome-system-monitor, it thinks the memory "really used" by one process is sum(info->mem) of X Server Memory(info->memxserver) and Writable Memory(info->memwritable), the "Writable Memory" is the memory blocks which are marked as "Private_Dirty" in /proc/PID/smapsfile.

查看一下,这是gnome-system-monitor的源代码,它认为一个进程“真正使用”的内存是info->memX Server Memory( info->memxserver)和Writable Memory( info->memwritable)的sum ( ),“ Writable Memory”是在/proc/PID/smaps文件中标记为“ Private_Dirty”的内存块。

Other than linux system, could be different way according to gnome-system-monitor code.

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

##代码##

回答by gheese

Take a look at smem. It will give you PSS information

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

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

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

回答by Edward Tomasz Napierala

Use the mincore(2) system call. Quoting the man page:

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

##代码##