原文:https://web.archive.org/web/20120520221529/http://emilics.com/blog/article/mconsumption.html
本文主要描述如何通过一个合理的方法来测量linux进程的内存消耗。linux包含了虚拟内存管理的特性,因此,内存消耗并没有想想中的那么简单。
心急速览
对于心急的人,我先给出结论。我倾向于使用PSS(Proportional Set Size)来测量内存消耗,前提是你的Linux系统支持。下面的Python方法,可以以KiB为单位返回指定进程ID的PSS。
import sys, re
def pss_of_process(pid):
with file('/proc/%s/smaps' % pid) as fp:
return sum([int(x) for x in re.findall('^Pss:\s+(\d+)', fp.read(), re.M)])
完整脚本:
https://web.archive.org/web/20130227090134/http://emilics.com/media/pub/pss.py
执行脚本,运行后获取所有用户运行进程PSS内存消耗(NEW BSD协议)
% sudo python pss.py
USER PID PSS COMMAND
postgres 1308 29521 postgres: writer process
root 1867 21983 /usr/bin/X :0 vt7 -nolisten tcp -auth /var/...
www-data 28752 14507 /usr/sbin/apache2 -k start
www-data 28753 13669 /usr/sbin/apache2 -k start
...
我们介绍三种概念来观测Linux进程的内存消耗。VSZ、RSS和PSS。
尽管有点不准确,但是我们考虑这样一个场景。假设有三个人共同租住1个房子。每个人代表一个进程,生活费代表内存消耗。在这个场景中,测量一个进程的内存消耗相当于计算1个人的生活费。
每个人都独自占有他们自己的电话线。VSZ、RSS、PSS这三个指标都会单独计算每个人的电话费用,这是没有任何问题的。
这个房子有一个车库,付费的人可以使用,但是他们都不开车,所以他们都不会使用这个车库。但是VSZ会在每个人的生活费上计算车库的费用,尽管车库只是留置给他们使用的。因此,VSZ代表了所有东西的总的生活费用,不管他们实际使不使用。RSS和PSS只计算他们实际上使用的费用,因此,他们不会计算车库的费用,因为没有使用。
因为他们一起使用网络和电视,他们均摊这些费用。但是RSS会将全部的费用计算在每个人的头上,即使他们是一起使用,而且还均摊费用。RSS就是假设网络和电视的费用是没有跟任何人分享的。
PSS只会每人计算1/3的网络和电视费用,因为他们均摊了。这个比RSS更合理一些。
我们使场景更复杂些,方便我们理解PSS的局限性。他们中一个人经常上网,但几乎不怎么看电视。因此理论上这个人应该付50%的网络费用和20%的电视费用。但是PSS无法表述这种情况。它只能简单的计算每个人1/3的网络费用和电话费用。
我认为使用PSS更加合理。但是有局限性,有些场景下使用RSS更好。当你想知道如果你搬出去自己生活需要多少生活费时,RSS会更加合理。
虚拟内存管理
通过 ps
命令查看进程信息
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
norisky 29065 0.1 0.2 8132 5604 pts/8 Ss 19:03 0:00 zsh
norisky 29075 0.0 0.1 10228 3772 pts/8 S+ 19:03 0:00 vi
norisky 29526 8.0 0.5 27708 10676 pts/4 S+ 19:05 0:00 emacs -nw
norisky 29527 0.0 0.0 2976 1080 pts/6 R+ 19:05 0:00 ps aux
在man手册中,我们可以了解到VSZ和RSS与内存消耗有关。
VSZ virtual memory size of the process in KiB (1024-byte units). Device mappings are currently excluded; this is
subject to change. (alias vsize).
RSS resident set size, the non-swapped physical memory that a task has used (in kiloBytes). (alias rssize, rsz)
我们应该使用哪个?
技术术语
- 页
Linux内存管理中的一块内存。一般Linux系统上1页是4096字节(4KB) - 物理内存
指的是实际内存,也就是RAM,计算机主存。 - 虚拟内存
使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),无论实际内存还有多少,其他应用消耗使用了多少。虚拟内存页可以映射到物理内存页。因此,进程只需要考虑使用虚拟内存
VSZ和按需分页
使用VSZ来测量进程内存消耗没有太大的意义。由于存在按需分页这个特性,它会阻止非必要的内存消耗。
例如,emacs
文本编辑器有能够处理XML文件的方法。但是这些方法根本没有使用。当用户编辑普通文本时,将这些方法加载到物理内存完全没有必须。按需加载特性使得进程只有在使用时才加载对应的页。
首先,当程序启动时,系统给予进程一个虚拟内存空间,但是并不会将方法功能对应的内存页真的加载到物理内存空间。当程序执行虚拟内存中的方法功能时,CPU中的MMU
告诉Linux系统,内存页未加载。而后,Linux暂停进程,加载内存页到主存,将页映射到进程的虚拟内存,然后进程从暂停的位置启动运行。因此,进程并不需要知道它暂停了,只需要假设从虚拟内存加载方法并使用。
VSZ是指进程的整个虚拟内存大小,无论页是否被加载到实际的主存中。因此,它并不是一个真实的指标可以用来测量内存消耗,因为它包含了没有使用的页。
RSS和共享库
RSS描述了进程实际加载到物理内存的页数量。听起来,特别像进程的真实内存消耗,而且比VSZ更精准。但因为共享库或动态链接库的原因,RSS也没有那么简单。
库是一个包含特定功能的模块。例如,libpng.so
处理PNG图片的压缩和解压缩,libxml2.so
处理xml文件。通常不需要每个程序员都编写这些功能,他们可以使用别人写的库来实现他们的目的。
共享库是被多个程序或进程所共享的。例如,当两个同时运行的进程都想要使用 libxml2.so
中的XML处理功能时,Linux一旦加载libxml2.so
后,将它映射到两个进程的虚拟空间。这两个进程不需要关注他们是否和其他进程共享这些功能,因为他们可以访问这些功能,在他们自己的虚拟空间中使用这些功能。因为这个特性,Linux减少了不必要的重复内存页。
现在,我们回头之前的例子。emacs
拥有可以处理xml文件的功能。这是使用的 libxml2.so
共享库。这次,用户确实在处理xml文件,emacs
正在使用 libxml2.so
。同时,有两个后台进程也在使用 libxml2.so
。因为 libxml2.so
是共享库,Linux只需要加载libxml2.so
一次到物理内存,然后将该地址映射到这3个进程的虚拟空间就可以了。
如果我们这时查看emacs
的RSS,它将包含libxml2.so
的内存页。这并没有错,因为emacs
确实在使用它。但另外两个进程呢?并不只是emacs
在使用这些功能。如果你将这三个进程的RSS相加,libxml2.so
尽管只被加载到物理内存1次,却被累加计算了3次。
因此,RSS可以表明当进程独自运行,没有去其他进程共享任何库时,所需要的内存消耗。而在实际中,RSS会过度进程的内存消耗。用来测量一个进程的内存消耗并没有错,但是你需要牢记RSS的行为。
PSS
PSS是一个相对比较新的内存消息测量指标。并不是所有Linux都支持PSS,但如果支持,那它绝对派的上用场。PSS的原理是将共享页平均分配到所有使用它的进程上。
如果存在N个进程在使用共享库,那么每个进程占用1/N共享库内存页。
还是之前的例子,emacs
和另外两个进程共享libxml2.so
,因为存在3个进程,PSS会假定每个进程占用1/3的libxml2.so
的内存页。
我认为PSS相对RSS更加可靠。尤其是考虑系统的整体内存消耗时。例如,当你开发一个多进程的系统时,你想评估你需要为设备配备多大内存时,PSS比RSS更加合适。
ps
命令不会打印PSS。你可以使用pss.py
的脚本来查看进程的PSS。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)