打现在起数月内,笔者将会撰写一个系列专题,而这是开山第一篇。该系列叫做《挑战Windows极限》,描述Windows和应用程序对具体资源的使用方法、资源使用在许可和实现方面的限制、资源使用的测量方法,以及资源泄露的诊断方法等。为了能够有效地管理Windows系统,我们需要知道Windows系统是怎样管理物理资源的,例如CPU和内存等,还要知道Windows系统怎样管理逻辑资源的,例如虚拟内存、句柄,还有窗口管理器对象等。了解这些资源的限制,并对其使用情况进行追踪,有助于我们精确地掌握应用程序的资源使用状况,帮助我们给某个关键应用分配足够多的系统资源,还有助于我们找出导致资源泄露的应用程序。
物理内存
物理内存是计算机上的最重要的资源之一。Windows的内存管理器负责给活动进程、设备驱动,和操作系统自己分配内存。因为绝大多数系统所能访问的数据和代码远比物理内存多,所以从本质上来说,物理内存是代码和数据在其中运行的窗口。所以内存容量对性能有影响,因为如果进程或者操作系统所需的代码或者数据不存在,内存管理器就需要从磁盘中读取这些内容。
除了会对性能造成影响,物理内存的容量还会影响其他资源。例如,对于非分页池来说,这是由物理内存提供后备的操作系统缓冲,很明显,其容量会受到物理内存的限制。物理内存也会对系统的虚拟内存限制有影响,虚拟内存的大小等于物理内存容量、再加上所有页面文件的最大容量。物理内存还会对进程的最大数量具有间接的影响,笔者将会在今后的文章里专门提到线程和进程的限制。
Windows Server内存限制
Windows对于物理内存的支持,要受到诸如硬件限制、许可、操作系统数据结构,以及驱动程序的兼容性等方面的综合影响。MSDN网站的
Memory Limits for Windows Releases
文章对不同Windows版本、以及同一个版本的不同SKU的限制进行介绍。
我们可以查看所有Windows版本的不同SKU的物理内存支持许可。例如,32位Windows Server 2008标准版仅支持4GB,而32位Windows Server 2008数据中心版支持64GB。类似的,64位Windows Server 2008标准版支持32GB,而64位32位Windows Server 2008数据中心版支持2TB。目前来说,并没有多少系统拥有2TB内存,不过Windows Server性能产品组知道有两台服务器拥有那么多的内存,其中一台位于某地的实验室。该服务器的任务管理器如下图所示:
![image_4.png](https://i-blog.csdnimg.cn/blog_migrate/3ed62163af73a2e2bdbbfda54dcea05b.png)
32位的最大限制是128GB,Windows Server 2003数据中心版可以支持,这是因为在大内存的系统上,内存管理器用来追踪物理内存的结构,需要消耗更多系统虚拟地址空间。内存管理器把每个内存页的追踪数据保存在叫做PFN数据库的数组中,而且考虑到性能因素,会把整个PFN数据库映射到虚拟内存中。因为它用28字节的数据结构来代表每个内存页,128GB系统的PFN数据库需要将近930MB的空间。32位Windows拥有4GB的虚拟地址空间,由硬件所定义,默认划分为两半,其中一半供用户模式进程(例如Notepad)所使用,另一半供系统所使用。因此980MB的容量就要占据将近一半的系统虚拟地址空间(共2GB),只剩下约1GB空间可以用来映射内核、驱动程序、系统缓存和其他系统数据结构:
![image_38.png](https://i-blog.csdnimg.cn/blog_migrate/299c7e66ad18328bcc2ffb400523e3ad.png)
这也就是为什么当同一个SKU版本以4GB的调整选项引导时(也叫做4GT,在Boot.ini文件里配置/3GB或者/USERVA选项,或者配置Bcdedit命令的/Set IncreaseUserVa启动选项),其内存限制会更大,这是因为4GT选项会给用户模式进程分配3GB空间,而仅给系统保留1GB空间。
内存管理器可以仅把PFN数据库的一部分映射到系统地址空间,这样就可以提供更多的内存空间,这会增加复杂性,同时由于增加了映射和取消映射的操作,而可能导致性能的下降。由于直到现在,计算机所配备的内存容量才变得足够大,才需要考虑采用这种办法,但是因为在64位Windows中,并不需要强制把整个PFN数据库映射到系统地址空间,所以这种可以提供更多内存的办法仅供64位Windows使用。
64位Windows Server 2008数据中心版最多支持2TB内存,这不是由于硬件限制所造成的,而是因为微软对2TB进行了严格测试,而只支持2TB内存。在Windows Server 2008发布的时候,计算机所配备的最大内存基本上就是2TB,所以Windows将其作为物理内存的最大值。
Windows客户端内存限制
64位Windows客户端,不同SKU的内存支持也有所不同,Windows XP Starter版的内存支持最低,仅512MB,而Windows Vista旗舰版的内存支持最高,可达128GB。但是所有版本的32位Windows客户端SKU,包括Windows Vista、Windows XP和Windows 2000 Professional,最大支持4GB物理内存。标准的X86内存管理模式,最大可以支持4GB的物理地址访问。在早期,并不需要考虑在客户端提供超过4GB的支持,因为当时很少有计算机配备那么高的内存,哪怕是服务器。
但是在Windows XP SP2开发的过程中,已经可以预见客户端计算机将会配备超过4GB的内存,所以Windows产品组对超过4GB的Windows XP计算机进行大量的测试。Windows XP SP2还支持物理地址扩展(PAE)功能,该功能本来是为了在硬件上实现非执行(NX)保护,因为这是数据执行保护(DEP)的必要条件,但是该功能还可以支持超过4GB的内存。
Windows产品组的工程师发现,很多测试计算机会发生崩溃、挂起,或者无法启动的故障现象,这是因为某些设备驱动程序,主要是一些客户端计算机(而非服务器)上的显卡或者声卡,其驱动程序在编写时没有考虑到内存大于4GB的情况。所以,这些驱动程序会截去那部分地址,从而导致内存冲突以及其他副作用。而服务器则通常会配备更加常规的硬件设备,其驱动程序更加简单稳定,因为通常来说碰到这些问题的几率很小。由于客户端设备驱动程序所存在的这些问题,迫使Windows客户端SKU只能忽略高于4GB的那部分物理内存,哪怕从理论上来说可以对其进行寻址。
32位客户端计算机的实际内存限制
尽管从许可上来说,32位客户端SKU的最大内存支持是4GB,但是实际上的限制会更低,这要看计算机的芯片组以及所连接的设备。这是因为物理地址映射不仅仅包含物理内存,还包含设备内存,X86和X64位系统会把所有设备内存映射到低于4GB的地址边界,以便确保和32位操作系统的兼容性,这些操作系统不知道如何处理超过4GB的地址部分。如果计算机配备4GB内存和类似显卡、声卡和网卡这样的设备,Windows会给这些设备内存分配共计500MB空间,而4GB物理内存中的500MB只能占用超过4GB的地址边界,如下图所示。
![image_10.png](https://i-blog.csdnimg.cn/blog_migrate/2f30fc937ec6b6f9944004c741356c4a.png)
其结果是,如果计算机拥有3GB或者更多内存,同时又运行32位Windows客户端操作系统,我们可能无法享受到所有内存。在Windows 2000、Windows XP和Windows Vista RTM系统上,我们可以在系统属性对话框、任务管理器的“性能”标签页上看到可以访问的物理内存,在Windows XP和Windows Vista(包含SP1)中,我们还可以在Msinfo32和Winver工具窗口里看到这些信息。在Windows Vista SP1中,其中某些工具会显示系统所安装的物理内存,而不是可以使用的内存,可以参考这篇
微软知识库文章
。
在笔者的4GB笔记本电脑中,如果启动到32位Windows Vista,可用的物理内存是3.5GB,可以在Msinfo32工具中看到。
![image_18.png](https://i-blog.csdnimg.cn/blog_migrate/752beeab4c6af0157268bc3c766b29ef.png)
我们可以使用
Alex Ionescu
所开发的
Meminfo
工具来查看物理内存的分配情况(这哥们将会参与编写《Windows Internals》的第五版,原本由笔者和
David Solomon
合写)。在这台笔记本电脑上运行Meminfo,并加上-r参数以便转储物理内存的分配情况,结果如下图所示:
![image_16.png](https://i-blog.csdnimg.cn/blog_migrate/fd71644e8b1bc778ad1d94c9d5f56b00.png)
注意其中内存地址范围中存在两个缺口,其中一个从页9F0000到页100000,另一个缺口从DFE6D000到FFFFFFFF(4GB)。但是,如果启动到64位Windows Vista,所有的4GB内存都可以使用,剩余的500MB物理内存位于高于4GB边界的地址部分,我们可以看到Windows是如何使用这500MB物理内存的:
![image_20.png](https://i-blog.csdnimg.cn/blog_migrate/196b8fec377b8a36285ade7611d9e3f1.png)
到底是谁占据了4GB以下的内存地址?设备管理器可以回答这个问题。要访问该工具,可以运行“devmgmt.msc”,在“查看”菜单中选择“依连接排序资源”选项,然后展开“内存”节点。在笔者的这台笔记本上,最大的映射设备内存,果然是显卡产生的,共占据256MB内存,从E0000000-EFFFFFFF:
![image_22.png](https://i-blog.csdnimg.cn/blog_migrate/c178ea0730b962ddd50e3c602459ae45.png)
其他设备占用其他大部分的地址空间,PCI总线会保留一部分地址范围,以供系统引导时某些设备固件所使用。
在带超级显卡的高端游戏计算机上,4GB以下的内存地址会减少很大一块。举个例子,笔者曾经购买过一台游戏计算机,带4GB内存和两块1GB的显卡。在采购时,笔者并没有指定操作系统版本,还以为他们会安装64位版本的Windows Vista,但是实际上安装的是32位版本,结果Windows只能访问2.2GB的内存。安装64位Windows后,我们可以在Meminfo的结果中看到从8FEF0000到FFFFFFFF存在如此大的内存空洞:
![image_24.png](https://i-blog.csdnimg.cn/blog_migrate/ccbc4cb678da6e6d18df444ec7668a51.png)
设备管理器显示,在2GB的内存空洞中,有512MB是显卡所占用的(每块显卡占用256MB),看起来设备固件保留其他更多的动态映射内存:
![image_26.png](https://i-blog.csdnimg.cn/blog_migrate/3cffb5722918e45988cbf38768f1ed6c.png)
哪怕计算机只有2GB内存,在32位Windows中也无法使用所有的内存,因为芯片组会为设备强制保留一部分内存范围。我们的一台家庭公用计算机,几个月前从一家OEM厂商那里购买的,显示安装了2GB内存,但是只有1.97GB是可用的:
![image_30.png](https://i-blog.csdnimg.cn/blog_migrate/1d1388e6bcd954e5172b4a1dda843b6d.png)
其中7E700000到FFFFFFFF的物理地址范围是给PCI总线和设备所保留的,理论上还有最多7E700000字节(1.976GB)的物理地址空间,但是其中还有一些还要给设备内存所保留,这就是为什么Windows报告说1.97GB。
![image_32.png](https://i-blog.csdnimg.cn/blog_migrate/1491cdfe851c38183ff8f59f2b3ba2df.png)
因为设备厂商需要向微软硬件质量实验室(WHQL)同时递交32位和64位驱动程序,才能获得驱动程序签名认证,目前绝大多数设备驱动程序能够处理超过4GB边界的物理地址。但是,32位Windows会继续忽略超出4GB的内存空间,因为很难衡量这样做的风险,如果没有问题的话,OEM厂商应该转向64位Windows。
使用64位Windows,我们能够完全使用所有的系统内存(SKU的最大限制),而不管容量多大,如果我们要购买高端游戏计算机,则应该请OEM厂商预装64位Windows。
是否拥有足够的内存?
不管系统拥有多少内存,问题是内存是否足够?不幸的是,我们没有快又准的方法来确切地了解。这里只有一个大致的办法可以使用,该方法基于对系统“可用”内存的长期监控,特别是在运行内存密集型负载的时候。如果物理内存没有分配给进程、内核,或者驱动程序,则Windows会把这部分物理内存定义为可用内存。顾名思义,可用内存可以在需要时分配给某个进程或者系统。Windows当然会对这部分内存进行最大限度的利用,会将其用作文件缓存(备用列表),还有存放清零内存(清零页列表),另外,Windows Vista还会使用SuperFetch功能,把数据和代码预加载到备用列表中,确保今后会使用的代码和数据,得到优先处理。
如果可用内存变少了,这说明进程或者系统正在大量消耗内存,如果可用内存在相当长的时间内都接近0,则可以考虑添加内存,有助于增加性能。有很多方法可以追踪可用内存。在Windows Vista里,我们可以在任务管理器里查看“物理内存使用记录”,以便间接地追踪可用内存,确认其是否长期逼近100%。笔者的8GB桌面计算机的任务管理器如下图所示(嘿嘿,是不是觉得偶的内存太多了哈!):
![image_36.png](https://i-blog.csdnimg.cn/blog_migrate/2f7c3af41b91c2bd6560399ab69f4fdb.png)
在所有版本的Windows中,我们可以使用性能监视器来查看可用内存,只需在Memory计数器组中添加Available Bytes计数器即可:
![image_34.png](https://i-blog.csdnimg.cn/blog_migrate/692ecd03d0d7bff0849dabe5ab6b3604.png)
我们可以在
Process Explorer
的“System Information”对话框里查看可用内存的即时值,也可以在Windows Vista之前的Windows系统的任务管理器的“性能”标签页里查看可用内存大小。
挑战极限
在CPU、内存和磁盘这三大因素中,对总体系统性能来说,内存通常是最重要的。内存越多越好。64位Windows可以确保我们能够充分利用所有内存,而且64位Windows还有其他一些性能上的优势,这些笔者将会在今后的系列博客文章讲到有关虚拟内存限制的时候再提及。