1. 内存及虚存基本布局
提起虚存,大都能说出几条来。
对于32位系统,大多数操作系统都会将4GB的内存空间的一部分挪给内核使用,应用程序无法直接访问这一段内存,这部分内存空间称为内核空间。Windows默认情况下会将高地址的2GB分配给内核(也可配置为1GB),而Linux默认情况下将高地址的1GB空间分配给内核。参考阅读:《程序员的自我修养—链接、装载与库》第10章--内存
Linux进程地址空间分布图]:
windows进程地址空间分布图如下图所示:
2. Windows上的虚存概况
以windows平台为例,应用程序可以调用堆API、.NET的垃圾回收器或者C运行时malloc相关API来分配虚拟内存,但是所有这些实现都依赖于VirtualAlloc API的实现。当应用用完地址空间的时候,接着会调用VirtualAlloc,然后可能会报错(返回NULL地址). 使用TestLimit工具,http://download.sysinternals.com/files/TestLimit.zip, 该工具带命令行参数-r 会不断的调用VirtualAlloc,直至出错。当在32位操作系统下运行时,会耗掉整个2GB的地址空间:
我们注意到2010MB并不等于2GB,但是Testlimit的其它代码和数据,包括可执行码和系统DLL,应该是是造成结果不同的原因,使用进程管理器,可能会看到实际虚存消耗:
有些应用,像SQL Server或者微软的Active Directory,管理了大量的数据结构,如果加载到地址空间的数据越多,表现得会越好。因此,在Windows NT4 SP3 当中引入了boot启动选项,/3GB,它允许将4GB的地址空间中的3GB提供给用户态,也就是让系统(内核)地址空间减少了1GB。Windows XP和win2003还引入了选项 /userva,来迁移2GB和3GB之间的内存碎片。详见下图:
如果要充分利用2G以上的空间,进程必须在exe文件中设定大地址标识。因为2GB空间的高位始终是0,这个高位同时也是用户态自己的标识。如果超过了2G,高位将变成1,如果没有相关处理,将会出错。
所有的微软服务器产品以及一些数据集中的程序都设定了大地址标识,如chkdsk.exe, lsass.exe (目录服务会用到),Smss.exe(会话管理器进程), esentutl.exe(目录Jet数据库修复工具), 我们可以使用dumpbin工具来检查exe中的该标志值, dumpbin是Visual Studio自带的工具,如下图所示:
Testlimit同样也用上了大地址标识:
3. 64位环境下的虚存
64位windows下,地址空间远远不止4GB, 这时,windows可以把32位进程序的最大的4GB全部用上,而剩余的地址空间都留给操作系统的虚存。如果您在64位的windows下运行Testlimit,可以看到它可以利用所有的32位地址空间:
64位进程序使用的是64位字长的指针,它们的理论最大地址空间为2^^64,然而windows并没有为用户进程序和操作系统提供比较平均的地址空间,而是在此空间中划分了一部分区域给用户进程,另一部分划给不同的系统内存资源,如系统页表的入口(PTE),文件缓存,页缓冲池和非页缓冲池。
IA64和x64体系下的进程地址空间大小是不同的,其大小取决于应用程序对内存用于支撑地址空间的总体需要(页表中的页和缓冲翻译表 TLB)。对于x64体系(AMD),是8T的量,而IA64下,则是7168GB(7T)的量,中间有1T的差异,主要源于IA64下,有1TB的空间用于*页目录用于为系统的Wow64映射表保留。 IA64和x64版本的windows, 各种资源的地址空间,大概都是128GB (如非页池,分配的就是的28GB的地址空间),只有一个例外,文件缓冲,它分配的是1TB的地址空间。总体来看,64位进程的地址空间,看起来如下图所示:
可以明显的看出,图里边有很大的地址空间空档,可能会用于将来的地址扩展。当您运行64位版本的Testlimit,它会消耗8TB,那将是它能管理的地址空间范围: