像'ps'和'top'这样的工具会报告各种内存使用情况,例如VM大小和Resident Set Size.但是,这些都不是"真正的"内存使用情况:
程序代码在同一程序的多个实例之间共享.
共享库程序代码在使用该库的所有进程之间共享.
一些应用程序分离进程并与它们共享内存(例如,通过共享内存段).
虚拟内存系统使VM大小报告几乎无用.
当一个进程被换出时,RSS为0,这使得它不是很有用.
等等
我发现Linux报告的私有脏RSS是最接近"真实"内存使用的东西.这可以通过对所有Private_Dirty
值求和来获得/proc/somepid/smaps
.
但是,其他操作系统是否提供类似功能?如果没有,有哪些替代方案?特别是,我对FreeBSD和OS X感兴趣.
在OSX上,Activity Monitor为您提供了一个非常好的猜测.
专用内存肯定是仅由应用程序使用的内存.例如,堆栈内存和使用malloc()动态保留的所有内存以及类似的函数/方法(Objective-C的alloc方法)是私有内存.如果你分叉,私人内存将与你的孩子共享,但标记为copy-on-write.这意味着只要页面未被任何进程(父进程或子进程)修改,它们就会在它们之间共享.只要任一进程修改任何页面,就会在修改此页面之前复制该页面.即使这个内存与fork子节点共享(并且它只能与fork子节点共享),它仍然显示为"私有"内存,因为在最坏的情况下,它的每一页都会被修改(迟早)和然后它再次对每个进程都是私有的.
共享内存是当前共享的内存(相同的页面在不同进程的虚拟进程空间中可见)或者可能在将来共享(例如只读内存,因为没有理由不共享读取 - 只有记忆).至少我是如何阅读Apple的一些命令行工具的源代码的.因此,如果您使用mmap(或将同一内存映射到多个进程的类似调用)在进程之间共享内存,则这将是共享内存.但是,可执行代码本身也是共享内存,因为如果启动了应用程序的另一个实例,则没有理由不共享已经加载到内存中的代码(可执行代码页默认为只读,除非您运行的是调试器中的应用程序).因此,共享内存实际上是您的应用程序使用的内存,就像私有内存一样,但它可能另外与另一个进程共享(或者它可能没有,但为什么它不会计入您的应用程序,如果它被共享?)
实内存是当前"分配"到您的进程的RAM量,无论是私有还是共享.这可以是私有和共享的总和,但通常不是.您的进程可能会分配比它当前需要的内存更多的内存(这会加快将来对更多内存的请求),但这不会对系统造成任何损失.如果另一个进程需要内存并且没有可用的可用内存,那么在系统开始交换之前,它将从进程中获取额外的内存并为其分配另一个进程(这是一个快速且无痛的操作); 因此,你的下一个malloc调用可能会慢一些.实内存也可以小于私有和物理内存; 这是因为如果您的进程从系统请求内存,它将只接收"虚拟内存".只要你不使用它,这个虚拟内存就不会链接到任何真实的内存页面(所以malloc 10 MB的内存,只使用它的一个字节,你的进程只会得到一页,4096字节的内存) - 只有在您真正需要时才会分配其余部分).交换的其他内存也可能不计入实际内存(不确定),但它将计入共享和私有内存.
虚拟内存是在您的应用程序进程空间中认为有效的所有地址块的总和.这些地址可能链接到物理内存(也可以是私有或共享),或者它们可能没有,但在这种情况下,只要您使用该地址,它们就会链接到物理内存.访问已知地址之外的内存地址将导致SIGBUS,您的应用程序将崩溃.交换内存时,此内存的虚拟地址空间保持有效,访问这些地址会导致内存交换回来.
结论:
如果您的应用程序没有显式或隐式使用共享内存,则私有内存是应用程序所需的内存量,因为堆栈大小(或多线程时的大小)以及您为动态内存调用的malloc().在这种情况下,您不必非常关心共享或真实内存.
如果您的应用程序使用共享内存,并且这包括一个图形用户界面,例如,您的应用程序和WindowServer之间共享内存,那么您也可以查看共享内存.非常高的共享内存数可能意味着您目前在内存中加载了太多图形资源.
真正的记忆对应用程序开发没什么兴趣.如果它大于共享和私有的总和,那么这只不过是系统在处理内存时远离你的进程.如果它更小,那么你的进程已经请求了比实际需要更多的内存,这也不错,因为只要你不使用所有请求的内存,你就不会从系统"窃取"内存.如果它比共享和私有的总和小得多,你可能只考虑在可能的情况下请求更少的内存,因为你有点过度请求内存(再次,这不错,但它告诉我你的代码不是针对最小内存使用进行了优化,如果是跨平台,其他平台可能没有如此复杂的内存处理,因此您可能更喜欢分配许多小块而不是几个大块,或者更快地释放内存,所以上).
如果您对所有这些信息仍然不满意,您可以获得更多信息.打开终端并运行:
sudo vmmap
您的流程的进程ID在哪里.这将显示流程空间中每个内存块的统计信息,包括起始地址和结束地址.它还会告诉你这个内存来自哪里(一个映射文件?堆栈内存?Malloc'ed内存?你的可执行文件的__DATA或__TEXT部分?),KB的大小,访问权限以及它是否是私有的,共享或写时复制.如果它是从文件映射的,它甚至会为您提供该文件的路径.
如果您只想要"实际"RAM使用,请使用
sudo vmmap -resident
现在它将为每个内存块显示虚拟内存块的大小以及当前在物理内存中实际存在多少内存块.
在每个转储结束时,还有一个概述表,其中包含不同内存类型的总和.这个表在我的系统上现在看起来像这样:
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%的内存已被换出到磁盘)
在Linux上,您可能需要/ proc/self/smaps中的PSS(比例集大小)数字.映射的PSS是其RSS除以使用该映射的进程数.
Top知道如何做到这一点.它在Debian Linux上默认显示VIRT,RES和SHR.VIRT = SWAP + RES.RES =代码+数据.SHR是可以与另一个进程(共享库或其他内存)共享的内存.
此外,"脏"存储器仅仅是已经使用过和/或未被交换的RES存储器.
这很难说,但最好的理解方法是查看一个没有交换的系统.然后,RES-SHR是进程独占内存.但是,这不是一个很好的方式来查看它,因为你不知道SHR中的内存正被另一个进程使用.它可能表示仅由进程使用的未写入的共享对象页面.
你真的不能.
我的意思是,在进程之间共享内存......你是否会计算它.如果你不算数,那就错了; 所有进程的内存使用量之和不会是总内存使用量.如果算上它,你就算两次 - 总和不会是正确的.
我,我对RSS很满意.并且知道你不能完全依赖它......
您可以从/ proc/pid/smaps获取私有脏和私有的RSS