深入sql server 2000的内存管理机制(三)
http://msdn.microsoft.com/data/default.aspx?pull=/library/en-us/dnsqldev/html/sqldev_01262004.asp
内存区域
sql server是分2块区域来组织内存分配,分别是bpool (缓冲池区)和memtoleave (内存释放区),如果你使用awe内存,那么实际上有第三个区:windows awe支持的高于3gb的物理内存区。
缓冲池区是这3块内存区中最卓越的,是sql server最初分配的缓冲池供最初的数据页和索引页使用,并且被用来分配小于8k的内存。memtoleave 是由虚拟内存空间组成包括在用户内存空间没有被缓冲池区使用的内存空间之中。windows awe调用3gb以上内存空间的函数作为缓冲池区的扩展可以提供额外的内存空间缓存数据页和索引页。
当你启动sql server时,缓冲池区的上限是根据机器中物理内存推算或用户内存空间的大小。一旦缓冲池区的大小被确定,内存释放区就紧随其后,不至于被后面的缓冲池区保留部分划分成分散的碎片。然后缓冲池区又在内存释放区旁边,使用32块单独的保留区运行dll文件和其他在缓冲池区被预定时sql server中使用的虚拟内存空间。当缓冲池区被预留后,内存释放区被释放。这段区域被sql server内部用来扩展8k的数据页和分配给其他外部应用(就像:内存消费者是sql server主要引擎以外的sql server进程),比如:ole db providers,com对象等等.
因此,当sql server已经启动, bpool (缓冲池区)就被预留,但不提交,同时在该进程在虚拟内存空间内的memtoleave (内存释放区)其实是空闲区域.如果你通过性能分析器的 virtual bytes perform 计数器,在sql server启动后看sql server的进程,你会看到它可以反映bpool (缓冲池区)预留区.我看到人们有些惊慌,因为这个数值比较高—毕竟,它反映的不是本机器的总共物理内存就是最大的用户内存空间减去memtoleave (内存释放区).这是不用担心的,比较而言这只是保留区,没有提交的空间.就如我前面所述,保留空间只是地址空间—并没有真实的物理内存存储直到内存空间被提交. 随着时间的过去,内存空间被提交,bpool (缓冲池区)将会增加,知道该server原始启动时确定的上限.
监控sql server虚拟内存的使用
你可以通过 sql server:buffer manager\target pages perform计数器跟踪bpool (缓冲池区)确定的最大空间.因为server不同的部分需要内存, bpool (缓冲池区)提交8k大小的页(这是原始保留的直到提交的大小到达确定目标).你可以通过 sql server:buffer manager\total pages perform计数器跟踪bpool (缓冲池区)使用的提交虚拟内存, 你可以通过 pivate bytes计数器跟踪sql server进程使用的全部提交的虚拟内存.
因为, 大部分的sql server的虚拟内存的使用是来自于bpool (缓冲池区),通过上面2个计数器可以知道. 一般而言增长和平稳是一前一后地.(请牢记:当应用程序启动了对awe的支持, pivate bytes计数器不能反映总体sql server内存使用情况).如果total pages perform计数器是水平的而pivate bytes计数器是向上倾斜的,这一般表示正在从memtoleave (内存释放区)分配新的内存.这个分配过程会正常结束—例如:在server中分配相关联的线程堆栈作为附加的工作线程,这也有可能是一个内部测com对象或xproc的内存泄漏.如果一个程序因为memtoleave (内存释放区)耗尽而用完虚拟内存空间,这是由于内存泄漏或内存过度消费.(或者在memtoleave (内存释放区)中最大空闲块降低到默认线程堆栈0.5mb以下),这样server就不能产生新的工作线程,即使在sp_configure max worker threads 的值没有到.在这种情况下,如果server需要产生新的工作线程来执行一个工作请求—比如:处理一个对server的新的连接请求,这些工作将会延迟,直到server可以产生新的线程或其他的线程可以使用.这样,在有足够的memtoleave (内存释放区)释放或其他的工作线程可以有效的处理连接之前,系统会阻止一个用户连接到server,因为这个连接会超时中止(time out).
内存分配器
在server中一个内存的消费者初始化一个内存分配器,首先是产生一个内存对象来管理这些请求.当这个对象来分配这些请求,他在server的内存管理器中,从bpool (缓冲池区)或memtoleave (内存释放区)来履行这些请求.如果这些请求小于8k,这些请求通常在bpool (缓冲池区) 分配.如果请求需要8k或以上的内存空间, 这些请求通常在memtoleave (内存释放区)分配.因为一个单独的内存对象可以用来执行多次内存分配.所以有可能一次内存分配正好在8k以下(包括管理对象的消费)的请求被分配在memtoleave (内存释放区). 在sql server的处理空间中,内存消费者通常是内部的.换句话说,这些内存消费者和对象是sql server自己的规范需要消耗内存来执行任务,但也不一定都这样.也存在一些外部的消费者,就像我前面锁说的.通常,这些外部的内存消费者调用正常的win32 api内存函数来分配和管理内存,并且从memtoleave (内存释放区)分配内存空间,非常明显这是sql server程序中唯一有效的区域. 可是xprocs有特殊的异常处理,当一个xproc调用open data services(ods) srv_alloc api函数,这完全和其他的内存消费者一样. 一般而言srv_alloc api函数从bpool (缓冲池区)申请小于8k的内存,对于大的内存从memtoleave (内存释放区)分配.
内存管理者
当server运行时,内存管理者检查物理内存的剩余有效容量,以保证windows和其他的应用程序可以可以平稳的运行.这个有效内存的大小在4mb和10mb直接变化.(在windows 2003中更接近10mb)并且这个基于系统内核加载和bpool (缓冲池区)中的页生命周期.如果server上的有效物理内存空间在这个阀值以下,server会减少提交bpool (缓冲池区)页来收缩内存物理存储的使用(假设动态内存分配是激活的).内存管理者也保证提交的内存页在指定的时间点后空闲,这样在接受到一个新的分配请求时,就不再需要等待内存分配.通过”空闲”,我的意思是:内存页提交后,但没有被使用.没有使用的提交bpool (缓冲池区)页可以通过一个空闲页清单来跟踪.这样所有得新得页都来自空闲页清单, 内存管理者从bpool (缓冲池区)预留区提交更多得页直到全部得预留区被提交.你看:process:private bytes perfmon 计数器缓慢增长(通常是成直线的)就是这个原因.
在多cpu系统中,没一个都有自己单独的空闲页清单,当一个空闲页被要求响应一个应用程序的请求,首先在当前的cpu的空闲页清单中满足分配的空间被检查,然后是其他cpu的空闲页清单.这样更好的利用每个处理器的本地缓存,提高了在多处理器的server上提高稳定性.你可以通过sql server:buffer partition perform对象监视指定的bpool (缓冲池区)区,你可以可以通过sql server:buffer manager\free pages perform计数器监视所有的bpool (缓冲池区)区.
因此在sql server整个运行过程中,sql server的内存处理器(无论内存线程管理器或同其他的线程服务)监视系统的内存使用状态,确认合理的剩余空闲物理内存的数量给其他的系统和预留合理的空闲页响应新的内存请求.当server使用awe内存,有些内存状态必需改变. bpool (缓冲池区)区开始通过server的物理内存中获得和锁定内存,内存锁的数量的变化是根据服务器最大内存是否设置了.如果配置了, bpool (缓冲池区)区试图根据最大的服务器内存锁住相应数量的内存.如果没有配置, bpool (缓冲池区)区会锁住接近128mb内存的所有物理内存,只有少量的区域留给其他处理.然后bpool (缓冲池区)区使用高于3g的物理内存(awe内存)用作运行数据和索引的页文件. bpool (缓冲池区)区根据需要映射物理内存和虚拟内存,因此可以被32位的指针引用.
概述
你已经知道了:sql server内存管理器是一个难点.理解一个程序是如何分配和管理内存是了解一个程序如何工作的基础.内存是非常重要的资源,它的有效利用是一个可靠应用程序设计的基本因素,懂得一个应用程序内存管理机制会是你的程序设计如虎添翼.
一个开发者,他是如何影响你呢?理解server的内存管理机制赋予你如何写一个高效的应用程序和解决一些和内存相关联问题的洞察力. 比如说,在一个提高客户端连接速度的调试中,你提高sql server的默认网络包的大小为8k., 立即,sql server立刻开始在error log中写错误信息,提示预留虚拟内存在memtoleave (内存释放区)区有问题.在看到这样的信息后你立刻会知道这个改变至少是问题的一部分,因为你知道分配8k或更多的内存是在memtoleave (内存释放区)区. 这样sql server连接相关联的缓存也是来自这个区域,因为你已经配置网络包的大小太高了.配置默认的网络包适合于.net framework的sqlclinet provider 8kb.这种情况并不象听起来那么合理.实际上,这是非常常见的问题,由于memtoleave (内存释放区)缺乏内存空间而引起的,因为网络包的大小太大,至少有些部分是这样的.
同时,了解sql server划分内存的方法可以帮助你了解你定制在sql server中运行的代码是否在系统资源的临界值,比如数据缓存. 比如说,你建立一个扩展存储过程调用srv_pro()函数来分配内存.假设,暂时你代码中分配的缓存小于8kb.根据我们前面的讨论,我们知道扩展存储过程是从bpool (缓冲池区)来分配内存.—可以用作数据缓存.
了解sql server是如何管理内存的,可以帮助我们按大小排列系统的开始顺序.这可以帮助你计算你所需的物理内存的数量和如何分配和分区给sql server.比如:良好的理解awe内存分配和应用程序的内存调整的相对的优势和缺点,可以帮助确定你的服务器是否需要超过3gb的物理内存和是否需要在启动时加/3gb的参数.
构思和配置sql server的内存管理机制有一个主要的冲突在于构思和配置在sql server上运行的应用程序,了解server是如何运行的可以帮助你设计,建立和测试基于sql server的应用程序.
