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

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

  • 程序代码在同一程序的多个实例之间共享。
  • 共享库程序代码在使用该库的所有进程之间共享。
  • 一些应用程序分离进程并与他们共享内存(例如通过共享内存段)。
  • 虚拟内存系统使VM大小报告几乎没有用处。
  • 当一个进程被换出时RSS是0,使得它不是很有用。
  • 等等

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

但是,其他操作系统是否提供了类似的function? 如果不是,还有什么select? 特别是,我对FreeBSD和OS X感兴趣。

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

专用内存是确保仅由您的应用程序使用的内存。 例如,使用malloc()和可比较的函数/方法(Objective-C的alloc方法)dynamic保留堆栈内存和所有内存是私有内存。 如果你分叉,私有内存将与你共享,但是标记为copy-on-write。 这意味着只要一个页面不被任何一个进程(父进程或subprocess)所修改,它们之间就会被共享。 只要任一进程修改任何页面,该页面在被修改之前就被复制。 即使这个内存是与fork子共享的( 只能和fork子共享),它仍然显示为“private”内存,因为在最坏的情况下,它的每一页都会被修改(迟早),那么对于每个过程又是私密的。

共享内存是当前共享的内存(在不同进程的虚拟进程空间中可以看到相同的页面),或者将来有可能共享内存(例如,只读内存,因为没有不共享读的理由只有记忆)。 至less我是这样读取Apple的一些命令行工具的源代码的。 所以如果你在使用mmap的进程之间共享内存(或者把相同的内存映射到多个进程的可比较的调用),这将是共享内存。 然而,可执行代码本身也是共享内存,因为如果您的应用程序的另一个实例启动,没有理由不共享已经加载到内存中的代码(可执行代码页默认是只读的,除非您正在运行在debugging器中的应用程序)。 因此,共享内存就像应用程序一样使用内存,就像私有内存一样,但共享内存也可以与另一个进程共享(或者不共享,但是为什么共享内存不会计入您的应用程序?)

实际内存是当前“分配”给您的进程的RAM数量,不pipe是私有的还是共享的。 这可以是私人和共享的总和,但通常不是。 你的进程可能会有更多的内存分配给它比目前需要(这加快了未来更多的内存请求),但这不是系统的损失。 如果另一个进程需要内存并且没有空闲内存可用,那么在系统开始交换之前,它会将额外内存从进程中分离出来,并将其分配给另一个进程(这是一个快速而无痛的操作)。 因此你的下一个malloc调用可能会稍微慢一些。 真实内存也可以小于专用和物理内存; 这是因为如果你的进程从系统请求内存,它只会收到“虚拟内存”。 这个虚拟内存没有链接到任何真正的内存页面,只要你不使用它(所以malloc 10 MB的内存,只使用它的一个字节,你的进程将得到只有一个页面,4096字节的内存分配 – 其余的只有在你真正需要的时候才被分配)。 进一步交换的内存可能不计入实际内存(不知道这一点),但它会计入共享和私人内存。

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

结论:
如果您的应用程序没有显式或隐式地使用共享内存,则私有内存就是您的应用程序需要的内存量,因为堆栈大小(如果是multithreading)或者因为您为dynamic内存调用了malloc()。 在这种情况下,您不必为共享或真实内存而烦恼。

如果您的应用程序使用共享内存,并且包含一个graphics用户界面(例如,在应用程序和WindowServer之间共享内存),那么您也可以查看共享内存。 共享内存数量非常高可能意味着您目前在内存中加载了太多的graphics资源。

真正的内存对于应用程序开发并不感兴趣。 如果它大于共享和私有的总和,那么这就意味着系统在你的进程中被占用的内存是懒惰的。 如果它比较小,那么你的进程要求的内存比实际需要的要多,这也不错,因为只要你不使用所有请求的内存,你就不会从系统中“偷窃”内存。 如果它远小于共享和私有的总和,那么你可能只考虑在可能的情况下请求更less的内存,因为你有点过分的请求内存(同样,这并不坏,但它告诉我你的代码不是为了最小的内存使用而优化,如果它是跨平台的,其他平台可能没有这样复杂的内存处理,所以你可能更喜欢分配许多小块,而不是几个大块,或者更快地释放内存。上)。

如果你还不满意这些信息,你可以得到更多的信息。 打开一个terminal并运行:

 sudo vmmap <pid> 

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

如果你只想要“实际”的RAM使用,使用

 sudo vmmap -resident <pid> 

现在,它会显示每个内存块有多大的内存块是虚拟的,它有多less实际上是目前在物理内存中。

在每个转储的末尾还有一个概览表,其中包含不同存储器types的总和。 这个表格现在在我的系统上看起来像这样:

 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 =核心graphics,这些都是最有可能的窗口内容,button,图像和其他数据caching为快速绘制。 它通过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%的内存已被换出到磁盘)

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

你真的不能。

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

我,我很高兴与RSS。 而知道你不能完全依靠它…

您可以从/ proc / pid / smaps中获得私人的,私密的RSS

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

此外,“脏”的内存仅仅是已经使用的RES内存,和/或还没有被交换。

可能很难说,但理解最好的方法是看一个不交换的系统。 然后,RES – SHR是进程独占内存。 但是,这不是一个好的方法,因为你不知道SHR中的内存是否被另一个进程使用。 它可能表示仅由进程使用的未经书写的共享对象页面。

看看smem。 它会给你PSS信息

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

 for i in /proc/[0-9]*; do grep -q 'Private_Dirty' $i/smaps; if [ $? -ne 0 ]; then continue; fi; echo -n "${i}: "; awk '/Private_Dirty/ {print $2,$3}' $i/smaps | sed 's/ tB/*1024 gB/;s/ gB/*1024 mB/;s/ mB/*1024 kB/;s/ kB/*1024/;1!s/^/+/;' | tr -d '\n' | sed 's/$/\n/' | bc | tr -d '\n'; echo; done | sort -n -k 2 

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

如果你想要一个linux风格的/ proc / PROCESSID /状态输出,请执行以下操作:

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

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

检查一下,这是gnome-system-monitor的源代码,它认为一个进程真正使用的内存是X Server Memory( info->memxserver )和Writable Memory( info->memwritable )的sum( info->meminfo->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); }