排查 IIS 7.x 应用程序池中的本机内存泄漏问题
适用于: Internet Information Services 7.0 及更高版本
本文可帮助你确定 IIS 应用程序池中本机内存泄漏的原因。
概述
请务必记住,在 Web 应用程序为请求提供服务时,高内存分配是正常的。
当 IIS 应用程序池中发生内存泄漏时,增加物理内存 (RAM) 无效,因为此方案中的内存不是物理内存 (RAM) 而是虚拟内存。 下表汇总了来自 Web 应用程序的可寻址内存的虚拟内存。
流程 | Windows | 具有大型地址感知进程) 的可寻址内存 ( | 虚拟字节的实际限制 | 专用字节的实际限制 |
---|---|---|---|---|
32 位 | 32 位 | 2 GB | 1400 MB | 800 MB |
32 位 | 具有 /3GB 的 32 位 | 3 GB | 2400 MB | 1800 MB |
32 位 | 64 位 | 4 GB | 3400 MB | 2800 MB |
64 位 | 64 位 | 8 TB | 不适用 | 不适用 |
应用场景
你始终会看到 Process\Private Bytes 和 Process\Virtual Bytes 都在增加,或者 Process\Private Bytes 和 Process\Working Set of w3wp.exe 正在增加,并且内存\可用字节数正在减少。
它可能会导致 IIS 中的应用程序池出现内存不足异常。
若要恢复,必须重启应用程序池,但执行此操作后,内存使用量再次攀升至较高水平。 看到上述错误时,应用程序池已自动重启。
数据收集
遇到内存使用率过高时,应首先确定 IIS 中应用程序池上工作进程的内存是否泄漏。 可以使用 性能监视器。 有关使用性能监视器的详细信息,请参阅分析性能数据。
提示
如果需要确定哪个应用程序池与特定的 w3wp.exe 进程相关联,请打开管理命令提示符,切换到 %windir%\System32\inetsrv 文件夹 (cd %windir%\System32\inetsrv) 并运行 appcmd list wp
。 这将在引号中显示w3wp.exe进程的进程标识符 (PID ) 。 可以将该 PID 与任务管理器中提供的 PID 匹配。
确认 w3wp.exe 进程内存使用率过高后,需要提供两条信息,以确定导致问题的原因。
- 性能监视器数据收集器集。
- w3wp.exe 进程的用户模式内存转储。
这两个都需要从内存使用率低(例如启动进程)收集,直到内存使用率过高(例如遇到内存不足异常)。
收集性能监视器数据收集器集
性能监视器 (Perfmon) 数据可以实时查看,也可以在数据收集器集中收集,以便稍后查看。 若要排查内存使用率过高的问题,需要收集数据收集器集。 若要创建数据收集器集以排查内存过高问题,请执行以下步骤:
- 从 Windows 控制面板打开管理工具。
- 双击性能监视器。
- 展开“ 数据收集器集” 节点。
- 右键单击“ 用户定义 ”,然后选择“ 新建>数据收集器集”。
- 输入 “高内存 ”作为数据收集器集的名称。
- 选择“ 手动创建 (高级) ”单选按钮。
- 选择“下一步”。
- 选择“ 创建数据日志 ”单选按钮。
- 选中“性能计数器 ”复选框。
- 选择“下一步”。
- 选择“添加”按钮。
- 从计数器列表中展开 “进程 ”。
- 从 Thread 对象中选择专用字节、虚拟字节和工作集。
- 从实例>列表中选择“<所有实例”。
- 选择“添加”。
- 从计数器列表中展开 “内存 ”。
- 从 Thread 对象中选择“可用字节数”。
- 选择“添加”。
- 选择“确定”。
- 将 “采样间隔 ”设置为 “1 秒 ”,然后将 “下一步 ”和“完成”。
现在,对话应如下所示:
创建调试诊断 1.2 规则
发生高内存情况时,收集用户模式进程转储的最简单方法是使用调试诊断 (DebugDiag.) 可以下载 调试诊断工具 v2 Update 3。
在服务器上安装 DebugDiag 并运行它。 (安装后,你将在 “开始 ”菜单上找到它。)
要确定哪个函数导致内存泄漏,最重要的信息是堆分配的堆栈跟踪。 默认情况下,不会获取这些堆栈跟踪。 可以按进程启用此功能。 使用以下命令启用堆栈跟踪:
gflags -i w3wp.exe +ust
命令不会为已在运行的进程启用堆栈跟踪。 因此,对于无法重启 (例如服务、lsass、winlogon) 的进程,必须重启测试计算机。
使用以下命令验证已为特定进程设置了哪些设置:
gflags -i w3wp.exe
运行 DebugDiag 时,将显示 “选择规则类型 ”对话框。 按照以下步骤为应用程序池创建泄漏规则:
选择 “本机 (non-.NET) 内存>句柄泄漏>下一步”。
选择一个进程,然后选择“ 下一步”。
选择“配置”。
设置规则,如下图所示:
可以根据需要调整这些值,但请注意不要指定少量的 MB 以生成大量转储文件。 当专用字节达到 800 MB 时,生成用户转储,此后每个额外的 100 MB。 当虚拟字节达到 1024 MB 时,生成用户转储,此后再增加 200 MB。
选择 “保存 & 关闭”。
选择“下一步”。
如果需要,请输入规则的名称,并记下转储的保存位置。 如果需要,可以更改此位置。
选择“下一步”。
选择“立即>激活规则完成”。
如果即使在获取内存转储时也遇到内存不足错误,则可以手动获取内存转储。
- 选择“ 处理 ”选项卡。
- 右键单击目标进程。
- 选择“ 创建完整用户转储”。
数据分析
出现内存不足错误或创建内存转储后,需要查看两组数据:Perfmon 数据收集器集和内存转储。 让我们首先查看 Perfmon 数据。
分析性能数据
若要查看问题的 Perfmon 数据,请右键单击“用户定义”节点下列出的“高内存”数据收集器集,然后选择“最新报告”。 可以看到类似于下图的内容:
若要了解导致 CPU 使用率过高问题的根本原因,请查看使用 DebugDiag 创建的转储。
使用 DebugDiag 进行转储分析
DebugDiag 能够通过执行自动转储分析来识别许多问题。 对于此特定问题,DebugDiag 的性能分析器非常适合帮助确定 CPU 使用率过高问题的根本原因。 若要使用分析器,请执行以下步骤:
- 在 DebugDiag 中选择“ 高级分析 ”选项卡。
- 选择“ 内存压力分析器”。 请确保使用 MemoryAnalysis.asp 而不是 DotNetMemoryAnalysis-BETA.asp。
- 选择 “添加数据文件”。
- 浏览到创建转储的位置。 默认情况下,这是 C:\Program Files\DebugDiag\Logs 文件夹的子文件夹。
- 选择其中一个转储,然后选择 Ctrl+A 以选择该文件夹中的所有转储。
- 选择 “打开”。
- 选择“ 开始分析”。
DebugDiag 需要几分钟时间来分析转储并提供分析。 完成分析后,会看到类似于下图的页面:
注意
报表的顶部指示检测到内存泄漏。 在目录中,你将看到一个指向泄漏分析报告详细信息的链接。 选择该链接,你将看到有关按分配计数或分配大小划分的前 10 个模块的信息。 下面是示例:
从此分析中,可以看到 leakcom 组件正在运行。 若要进一步查看 leakcom 的 模块信息 ,如下所示,可以看到该方法 CFoo::crtheap
分配了未完成的内存。
下一步是查看方法的代码 CFoo::crtheap
。 执行此操作时,会发现以下代码片段:
STDMETHODIMP CFoo::crtheap(void)
{
malloc(1024 * 10);
return S_OK;
}
上述代码肯定会导致内存泄漏,因为分配的内存不会释放。
提示
如果启用堆栈跟踪 (gflags -i w3wp.exe +ust
) ,则可以通过使用 WinDBG 分析转储来查看以下调用堆栈。 如果默认禁用堆栈跟踪,则永远不会看到以下调用堆栈。
0:000> !heap -p -a 42ae5b28
address 42ae5b28 found in
_HEAP @ 6690000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
42ae5b28 0541 0000 [00] 42ae5b40 02800 - (busy)
77e9df42 ntdll!RtlAllocateHeap+0x00000274
75133db8 msvcr90!malloc+0x00000079
513f3cc7 LeakTrack!CCRTMemoryLT::R90mallocDetour+0x00000067
75933cef oleaut32!CTypeInfo2::Invoke+0x0000023f
61f527b8 leakcom!ATL::IDispatchImpl::Invoke+0x00000058
f05cb4d vbscript!IDispatchInvoke+0x00000059
f053f40 vbscript!InvokeDispatch+0x000001a5
f060795 vbscript!InvokeByName+0x00000043
f06080d vbscript!CScriptRuntime::RunNoEH+0x000022cf
f054122 vbscript!CScriptRuntime::Run+0x00000064
f054099 vbscript!CScriptEntryPoint::Call+0x00000074
f054279 vbscript!CSession::Execute+0x000000c8
f0544c0 vbscript!COleScript::ExecutePendingScripts+0x00000146
f052013 vbscript!COleScript::SetScriptState+0x0000014d
513023c0 asp!CActiveScriptEngine::TryCall+0x00000019
513027b3 asp!CActiveScriptEngine::Call+0x000000e7
513022c7 asp!CallScriptFunctionOfEngine+0x0000003e
513063d5 asp!ExecuteRequest+0x0000014a
51302676 asp!Execute+0x000001c4
513028f2 asp!CHitObj::ViperAsyncCallback+0x000003fc
51302030 asp!CViperAsyncRequest::OnCall+0x0000006a
563de19 comsvcs!CSTAActivityWork::STAActivityWorkHelper+0x00000032
771304fb ole32!EnterForCallback+0x000000f4
771284c7 ole32!SwitchForCallback+0x000001a8
77126964 ole32!PerformCallback+0x000000a3
7713df32 ole32!CObjectContext::InternalContextCallback+0x0000015b
771f47ef ole32!CObjectContext::DoCallback+0x0000001c
563dfbd comsvcs!CSTAActivityWork::DoWork+0x0000012f
563e51b comsvcs!CSTAThread::DoWork+0x00000018
563f24d comsvcs!CSTAThread::ProcessQueueWork+0x00000037
563f4c0 comsvcs!CSTAThread::WorkerLoop+0x00000135
773a1287 msvcrt!_endthreadex+0x00000044
总结
通过使用 Perfmon 和 DebugDiag,可以轻松收集有助于确定应用程序池中内存泄漏的原因的数据。 如果使用这些技术找不到根本原因,可以向 Microsoft 开具支持票证。 请务必在支持票证中包含 Perfmon 数据和堆栈跟踪转储,以帮助缩短周转时间。
其他资源
反馈
https://aka.ms/ContentUserFeedback。
即将发布:在整个 2024 年,我们将逐步淘汰作为内容反馈机制的“GitHub 问题”,并将其取代为新的反馈系统。 有关详细信息,请参阅:提交和查看相关反馈