如你所知,内存泄露是由于疏忽或错误造成程序未能释放已经不再使用的内存的情况,随后不能重新分配。随着时间推移,会导致系统内存池耗尽(分页或非分页),最终导致服务器中止。
当然,在Windows服务器中止之前,一般有其他内存泄露的症状。主要值得注意的是来自服务器服务(SRV组件)的系统事件日志的入口。尤其是注意:
Event ID 2019: The server was unable to allocate from the system nonpaged pool because the pool was empty
或者
Event ID 2020: The server was unable to allocate from the system paged pool because the pool was empty
两件事情预示着Windows内存泄露,需要立即进行调查。内存泄露的其他信号包括过高的页面文件使用率和递减的可用内存。
Perfmon
一般用于诊断内存泄露的首要工具是Perfmon,这是一款内置于Windows的图形工具。通过在适当的计数器上收集性能参数,你能确定内存泄露是否是由于用户进程(应用)或者内核模式驱动造成的。性能参数能使用计数器收集到后台,并写入日志文件。随后由Perfmon或来自CodePlex的Performance Analysis of Logs (PAL)进行阅读。Microsoft KB article 811237文档解释了如何使用Perfmon记录性能计数器。微软也有款免费的工具PerfWiz提供建立Perfmon登录的向导。
如果你怀疑某个用户模式应用在泄露内存,能使用Perfmon收集Process object计数器、Pool Paged Bytes和Pool Nonpaged Bytes。这将显示是否任何进程继续分配页或非分页资源,不需要后来重新分配。如果你怀疑内核模式驱动在泄露内存,使用Perfmon收集Memory object计数器、Pool Nonpaged Bytes和Pool Paged Bytes。
在下面的例子中,Perfmon正用于监控内存对象的性能计数器,即分页或非分页池。通过右击计数器,你能调整比例让两种计数器都显示在同个图形上。如下图1所示,Pool Paged Bytes计数器(红线)持续增长而没有递减,意味着它在泄露内存。查看分页池计数器的最大值,它从118MB达到了最大值350MB。
因此这时,我们知道我们存在分页池泄露。然后我们能使用Perfmon检查Pool Paged Bytes的Process对象。如果在分页池使用率里没有进程显示相应的增长,我们就能断定驱动或内核模式代码在泄露内存。
Poolmon
为了进一步隔离内存泄露,我们需要确定哪个驱动在分配内存。当驱动分配内存时,它们插入一个四个字符的标签在内存池数据架构里,这样来识别分配内存的是哪个驱动。通过检查不同的池分配,你能确定哪个驱动分配了过多内存。要将这些标签与对应的驱动联系起来,参见Microsoft KB article 298102文档。你也能安装Windows的调试工具检查以下文件:
\Program Files\Debugging Tools for Windows\Triage\Pooltag.txt
Memory Pool Monitor工具(Poolmon)是微软提供的一款免费工具,查看池分配并以图显示对应驱动的结果。在下面的例子中,Poolmon正用于追踪泄露池标签“Leak”。Poolmon显示分配的数量、释放的数量、差异,及所分配字节的数量。如果设置正确,Poolmon也显示驱动的名称。
这里,我们能看到标签“Leak”属于Notmyfault.sys驱动,并且拥有超过83MB的页面池。
Windbg
假设你的服务器由于内存泄露而被锁定的话,你通常面临故障转储,随后如上一篇文章“如何解决Windows服务器崩溃的问题?”所述的进行分析。在使用内核调试器工具(Windbg)分析崩溃原因时,关键的事是查看内存池使用率和哪个数据架构消耗了资源。
在调试器里使用的第一个命令是!vm 1,如下图例子所示。这个命令将显示目前的虚拟内存使用率,尤其是非分页和分页池区域。调试器会标志任何过量的池使用率和任何池分配失效,如图3所示。如果使用率是或者接近最大值,由于耗尽资源,那么服务器就会崩溃。
最后,你能使用!poolused命令用调试器显示分页或者非分页池数据架构。命令的不同选项允许你指定分页或非分页池,并将输入进行分类。在下面的例子中,!poolused 5命令用于显示分页池数据架构,在图4中,你能看见标有“Leak”的数据架构在消耗最多的页面池(超过115MB),与之相关的驱动是notmyfault.sys。
如你所见,使用如Perfmon、PerfWiz、PAL, Poolmon和Windbg这样的工具能监控内存泄露,确定是否是分页或非分页内存,发现导致问题的驱动或应用。在这之后,联系软件厂商看他们是否有更新的驱动或者解决内存泄露的可用镜像。