在 OSX 上,活动监视器实际上给你一个很好的猜测。
私有内存肯定是仅由您的应用程序使用的内存。例如堆栈内存和所有使用 malloc() 和类似函数/方法(Objective-C 的分配方法)动态保留的内存都是私有内存。如果你分叉,私人内存将与你的孩子共享,但标记为写时复制。这意味着只要页面没有被任何一个进程(父进程或子进程)修改,它就会在它们之间共享。只要任一进程修改任何页面,该页面就会在被修改之前被复制。即使此内存与 fork 子共享(并且只能与 fork 子共享),它仍然显示为“私有”内存,因为在最坏的情况下,它的每一页都会被修改(迟早)并且然后它再次对每个进程都是私有的。
共享内存要么是当前共享的内存(相同的页面在不同进程的虚拟进程空间中可见),要么是将来可能共享的内存(例如只读内存,因为没有理由不共享 read -仅记忆)。至少我是这样从 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% 的内存已被换出到磁盘)。
原因很简单:映射文件中的只读内存不会被交换。如果系统需要内存,则简单地从进程中删除当前页面,因为内存已经“交换”了。它仅包含直接从文件映射的内容,并且可以在需要时重新映射此内容,因为文件仍然存在。这样,该内存也不会浪费交换文件中的空间。只有可写内存在被删除之前必须首先交换到文件,因为它的内容之前没有存储在磁盘上。