了解smaps内存之前要先搞清楚Linux内存管理中的虚拟内存(Virtual Memory)和驻留内存(Resident Memory)两个概念。
1、虚拟内存
首先需要强调的是虚拟内存不同于物理内存,虽然两者都包含内存字眼但是它们属于两个不同层面的概念。进程占用虚拟内存空间大并非意味着程序的物理内存也一定占用很大。虚拟内存是操作系统内核为了对进程地址空间进行管理(process address space management)而精心设计的一个逻辑意义上的内存空间概念。虚拟内存是逻辑意义上的内存空间,为了使程序能够在物理内存上运行,需要将虚拟内存映射到物理内存,这一功能由操作系统中页映射表来完成。
2、驻留内存
驻留内存,是指被映射到进程虚拟内存空间的物理内存。进程的驻留内存就是进程实实在在占用的物理内存。一般我们所讲的进程占用了多少内存,其实就是说的占用了多少驻留内存而不是多少虚拟内存。因为虚拟内存大并不意味着占用的物理内存大。
Linux下常常使用top命令来查看进程内存使用情况,top命令中有VIRT、RES和SHR三种内存,三者代表的内存分别如下:
VIRT:已经映射到物理内存空间的部分和尚未映射到物理内存空间的部分总和。
RES:进程虚拟内存空间中已经映射到物理内存空间的部分的大小。
SHR:进程占用的共享内存大小。
其中,SHR内存存在的原因,是Linux动态库的使用,
我们写的程序会依赖于很多外部的动态库(.so),比如libc.so、libld.so等等。这些动态库在内存中仅仅会保存/映射一份,如果某个进程运行时需要这个动态库,那么动态加载器会将这块内存映射到对应进程的虚拟内存空间中。多个进展之间通过共享内存的方式相互通信也会出现这样的情况。
这么一来,就会出现不同进程的虚拟内存空间会映射到相同的物理内存空间。这部分物理内存空间其实是被多个进程所共享的,所以我们将他们称为共享内存,用SHR来表示。
通过top命令我们已经能看出进程的虚拟空间大小(VIRT)、占用的物理内存(RES)以及和其他进程共享的内存(SHR)。但是仅此而已,如果我想知道如下问题:
进程的虚拟内存空间的分布情况,比如heap占用了多少空间、文件映射(mmap)占用了多少空间、stack占用了多少空间?
进程是否有被交换到swap空间的内存,如果有,被交换出去的大小?
mmap方式打开的数据文件有多少页在内存中是脏页(dirty page)没有被写回到磁盘的?
以上这些问题都无法通过top命令给出答案,但是有时候这些问题正是我们在对程序进行性能瓶颈分析和优化时所需要回答的问题。针对这些问题,Linux的proc文件系统给出了解决方法,它给每个进程提供了一个smaps文件,通过分析该文件就可以一一回答以上问题。
1、swaps内存的概念
在smaps文件中,每一条记录(如下图所示)表示进程虚拟内存空间中一块连续的区域。其中第一行从左到右依次表示地址范围、权限标识、映射文件偏移、设备号、inode、文件路径。详细解释可以参见understanding-linux-proc-id-maps。
接下来8个字段的含义分别如下:
• Size:表示该映射区域在虚拟内存空间中的大小。
• Rss:驻留内存大小,是进程当前实际占用的物理内存大小,包括进程独自占用的物理内存、和其他进程共享的内存。
• Shared_Clean:和其他进程共享的未被改写的page的大小
• Shared_Dirty:和其他进程共享的被改写的page的大小
• Private_Clean:未被改写的私有页面的大小。
• Private_Dirty:已被改写的私有页面的大小。
• Swap:表示非mmap内存(也叫anonymous memory,比如malloc动态分配出来的内存)由于物理内存不足被swap到交换空间的大小。
• Pss:该虚拟内存区域平摊计算后使用的物理内存大小(有些内存会和其他进程共享,例如mmap进来的)。比如该区域所映射的物理内存部分同时也被另一个进程映射了,且该部分物理内存的大小为1000KB,那么该进程分摊其中一半的内存,即Pss=500KB。
2、查看swaps内存
Linux下使用如下命令查看swaps内存
cat /proc/{pid}/smaps > smaps.txt
输出如下图所示
通过查看进程的smaps文件,可以获取进程每个内存映射块的详细信息,分析这些信息,可帮助定位如内存泄漏等内存问题。