实际和以前的free命令是有点相似的。 不过free更专一应用,这个更专一理解。node
想必在linux上写过程序的同窗都有分析进程占用多少内存的经历,或者被问到这样的问题——你的程序在运行时占用了多少内存(物理内存)?一般咱们能够经过top命令查看进程占用了多少内存。这里咱们能够看到VIRT、RES和SHR三个重要的指标,他们分别表明什么意思呢?这是本文须要跟你们一块儿探讨的问题。固然若是更加深刻一点,你可能会问进程所占用的那些物理内存都用在了哪些地方?这时候top命令可能不能给到你你所想要的答案了,不过咱们能够分析proc文件系统提供的smaps文件,这个文件详尽地列出了当前进程所占用物理内存的使用状况。linux
这篇blog总共分为三个部分。第一部分简要阐述虚拟内存和驻留内存这两个重要的概念;第二部分解释top命令中VIRT、RES以及SHR三个参数的实际参考意义;最后一部分向你们介绍一下smaps文件的格式,经过分析smaps文件咱们能够详细了解进程物理内存的使用状况,好比mmap文件占用了多少空间、动态内存开辟消耗了多少空间、函数调用栈消耗了多少空间等等。ide
要理解top命令关于内存使用状况的输出,咱们必须首先搞清楚虚拟内存(Virtual Memory)和驻留内存(Resident Memory)两个概念。函数
首先须要强调的是虚拟内存不一样于物理内存,虽然二者都包含内存字眼可是它们属于两个不一样层面的概念。进程占用虚拟内存空间大并不是意味着程序的物理内存也必定占用很大。虚拟内存是操做系统内核为了对进程地址空间进行管理(process address space management)而精心设计的一个逻辑意义上的内存空间概念。咱们程序中的指针其实都是这个虚拟内存空间中的地址。好比咱们在写完一段C++程序以后都须要采用g++进行编译,这时候编译器采用的地址其实就是虚拟内存空间的地址。由于这时候程序尚未运行,何谈物理内存空间地址?凡是程序运行过程当中可能须要用到的指令或者数据都必须在虚拟内存空间中。既然说虚拟内存是一个逻辑意义上(假象的)的内存空间,为了可以让程序在物理机器上运行,那么必须有一套机制可让这些假象的虚拟内存空间映射到物理内存空间(实实在在的RAM内存条上的空间)。这其实就是操做系统中页映射表(page table)所作的事情了。内核会为系统中每个进程维护一份相互独立的页映射表。。页映射表的基本原理是将程序运行过程当中须要访问的一段虚拟内存空间经过页映射表映射到一段物理内存空间上,这样CPU访问对应虚拟内存地址的时候就能够经过这种查找页映射表的机制访问物理内存上的某个对应的地址。“页(page)”是虚拟内存空间向物理内存空间映射的基本单元。post
下图1演示了虚拟内存空间和物理内存空间的相互关系,它们经过Page Table关联起来。其中虚拟内存空间中着色的部分分别被映射到物理内存空间对应相同着色的部分。而虚拟内存空间中灰色的部分表示在物理内存空间中没有与之对应的部分,也就是说灰色部分没有被映射到物理内存空间中。这么作也是本着“按需映射”的指导思想,由于虚拟内存空间很大,可能其中不少部分在一次程序运行过程当中根本不须要访问,因此也就没有必要将虚拟内存空间中的这些部分映射到物理内存空间上。性能
到这里为止已经基本阐述了什么是虚拟内存了。总结一下就是,虚拟内存是一个假象的内存空间,在程序运行过程当中虚拟内存空间中须要被访问的部分会被映射到物理内存空间中。虚拟内存空间大只能表示程序运行过程当中可访问的空间比较大,不表明物理内存空间占用也大。优化
图1. 虚拟内存空间到物理内存空间映射spa
驻留内存,顾名思义是指那些被映射到进程虚拟内存空间的物理内存。上图1中,在系统物理内存空间中被着色的部分都是驻留内存。好比,A一、A二、A3和A4是进程A的驻留内存;B一、B2和B3是进程B的驻留内存。进程的驻留内存就是进程实实在在占用的物理内存。通常咱们所讲的进程占用了多少内存,其实就是说的占用了多少驻留内存而不是多少虚拟内存。由于虚拟内存大并不意味着占用的物理内存大。操作系统
关于虚拟内存和驻留内存这两个概念咱们说到这里。下面一部分咱们来看看top命令中VIRT、RES和SHR分别表明什么意思。设计
搞清楚了虚拟内存的概念以后解释VIRT的含义就很简单了。VIRT表示的是进程虚拟内存空间大小。对应到图1中的进程A来讲就是A一、A二、A三、A4以及灰色部分全部空间的总和。也就是说VIRT包含了在已经映射到物理内存空间的部分和还没有映射到物理内存空间的部分总和。
RES的含义是指进程虚拟内存空间中已经映射到物理内存空间的那部分的大小。对应到图1中的进程A来讲就是A一、A二、A3以及A4几个部分空间的总和。因此说,看进程在运行过程当中占用了多少内存应该看RES的值而不是VIRT的值。
最后来看看SHR所表示的含义。SHR是share(共享)的缩写,它表示的是进程占用的共享内存大小。在上图1中咱们看到进程A虚拟内存空间中的A4和进程B虚拟内存空间中的B3都映射到了物理内存空间的A4/B3部分。咋一看很奇怪。为何会出现这样的状况呢?其实咱们写的程序会依赖于不少外部的动态库(.so),好比libc.so、libld.so等等。这些动态库在内存中仅仅会保存/映射一份,若是某个进程运行时须要这个动态库,那么动态加载器会将这块内存映射到对应进程的虚拟内存空间中。多个进展之间经过共享内存的方式相互通讯也会出现这样的状况。这么一来,就会出现不一样进程的虚拟内存空间会映射到相同的物理内存空间。这部分物理内存空间实际上是被多个进程所共享的,因此咱们将他们称为共享内存,用SHR来表示。某个进程占用的内存除了和别的进程共享的内存以外就是本身的独占内存了。因此要计算进程独占内存的大小只要用RES的值减去SHR值便可。
经过top命令咱们已经能看出进程的虚拟空间大小(VIRT)、占用的物理内存(RES)以及和其余进程共享的内存(SHR)。可是仅此而已,若是我想知道以下问题:
以上这些问题都没法经过top命令给出答案,可是有时候这些问题正是咱们在对程序进行性能瓶颈分析和优化时所须要回答的问题。所幸的是,世界上解决问题的方法总比问题自己要多得多。linux经过proc文件系统为每一个进程都提供了一个smaps文件,经过分析该文件咱们就能够一一回答以上提出的问题。
在smaps文件中,每一条记录(以下图2所示)表示进程虚拟内存空间中一块连续的区域。其中第一行从左到右依次表示地址范围、权限标识、映射文件偏移、设备号、inode、文件路径。详细解释能够参见understanding-linux-proc-id-maps。
接下来8个字段的含义分别以下:
图2. smaps文件中的一条记录
有了smap如此详细关于虚拟内存空间到物理内存空间的映射信息,相信你们已经可以经过分析该文件回答上面提出的4个问题。
最后但愿全部读者可以经过阅读本文对进程的虚拟内存和物理内存有一个更加清晰认识,并能更加准确理解top命令关于内存的输出,最后能够经过smaps文件更进一步分析进程使用内存的状况。