为什么IIS在池回收时没有清理旧的工作进程(w3wp.exe),导致网站出现内存不足异常?

29
我有一个asp.net-mvc网站,最近在我的Web服务器上遇到了内存不足的异常。我只有1个应用程序池并且我们最近将IIS设置为在达到某个限制后重新启动。有一天我进去看到有4个w3wp.exe进程正在运行(每个进程都使用约1.8GB内存)。
我认为在重新启动过程中,它没有杀死旧的工作进程,最终导致我的网站出现内存不足异常,因为这台机器只有8GB内存。我可以向计算机添加内存,但我担心为什么这些旧进程没有被清理。
有没有建议来找出为什么这个重新启动过程没有杀死旧的w3wp.exe进程并让它们继续运行?有关了解根本原因甚至避免此风险的任何建议或解决方法吗?

1
你有长时间运行的进程吗?--“默认情况下,IIS将使用重叠回收方法,保持旧进程直到当前请求处理完成(或经过一段设置的超时时间),而新进程处理新请求。这确保了服务的连续性,通常您不会注意到回收。” - Ross Bush
我确实有一些长时间运行的进程,但工作进程在回收后仍停留了一个小时。我的长进程不到1分钟。 - leora
您可以通过IIS管理UI(Worker Processes实用程序)检查给定站点/应用程序池的当前(挂起)请求。以下是一个示例链接:https://dev59.com/CGUo5IYBdhLWcg3w-zii - Simon Mourier
你是如何管理资源的?例如,EF上下文,你是如何访问它们的?你是在WEB API端点中实例化上下文还是在服务中实例化?你使用依赖注入容器吗?如果是这样,它有什么样的生命周期?每个请求,每个InstancePerLifetime? - Dave Alperovich
5个回答

7
应用程序池回收通常会关闭旧的工作进程,因此我认为您没有遇到IIS的错误。请注意,如果进程变成孤儿进程,则可以存活更长时间,并且根据配置,IIS必须将它们留在那里供您进行故障排除。

https://www.iis.net/configreference/system.applicationhost/applicationpools/add/failure

如果您对调试此类进程不太熟悉,我建议您通过http://support.microsoft.com开启支持案例,让微软的支持人员帮助您解决问题。

3
你应该追踪实现了IDisposable接口的类,以及任何未调用Dispose()方法的使用。我曾遇到过类似问题,并已在这个问题的回答中进行了发布。
最有可能的原因是你没有在数据库连接上调用.Dispose()方法。人们往往会对此感到困惑,因为.NET内置的连接池使得你听起来不需要释放连接。然而,在请求结束时(或者当你完成使用连接时)调用.Dispose()正是你应该做的,以防止资源泄漏。连接池期望这种情况发生。
我认为4个w3wp.exe进程并不是什么大问题——IIS生成多个进程是正常的。但显然你的应用程序存在需要解决的资源泄漏问题。首先看看我上面提到的IDisposable,如果仍有问题,检查一下缓存中的项目,并尝试确定是否可以使用更有效的缓存策略。如果所有其他方法都失败了,请对应用程序进行性能分析,以确定资源泄漏的来源。很可能你的应用程序应该在某个地方实现了IDisposable

3
当我运行类似FFMpeg.exe或一些WPF图形的PDF转换时,我遇到了相似的问题,IIS进程无法关闭并会发出内存未找到错误。问题不在于IIS,而在于进程中的某些死锁,即使崩溃后也会阻塞程序。
解决方法是将您的网站分成两个独立的网站,一个仅执行与通常不会崩溃的数据库进行事务处理的逻辑。像视频/照片转换、PDF转换或任何可能导致崩溃的逻辑应移动到其他Web服务中。并且使用HTTP在网站内部调用Web服务来处理它们。
现在,在这种情况下,仍然无法避免Web服务进程崩溃,因此我决定每100个请求重置应用程序池工作程序(在查看了一些请求后选择了这个数值,平均来说,只有在达到200个请求之后才会超过1GB),并且将应用程序池转换为Web Garden,通过每个池设置4个进程。
这种设置的优点是,您可以轻松地将Web服务进程移到其他机器上,您可以增加/减少每个池的进程数量。而且,主网站仅执行事务处理,因此不受Web服务进程重启的影响,因此响应速度非常快。

2
从您的帖子中可以清楚地看出,将IIS设置为在达到一定限制后进行回收会创建一个新的应用程序池。
旧进程无法终止可能有多种原因。
其中一个可能是内存泄漏,未处理非托管资源,您可以查找此类问题。
要找出另一个原因,请在IIS中启用“进程孤立”功能。您可以使用“Process Explorer”查找罪魁祸首进程。
您还可以选择设置一个可执行文件,在进程被孤立或遗弃时执行该文件。
即时解决方案也可以是在某个CPU限制后将操作设置为KillW3p。
<applicationPools>
   <add name="DefaultAppPool">
     <cpu limit="80000" action="KillW3wp" resetInterval="00:02:00" />
   </add>
</applicationPools>

0

在任务管理器中右键单击“旧的” w3wp 进程,选择“创建转储”,然后在 Visual Studio 中探索该转储(只需启动一个空的 Visual Studio,然后将 DMP 文件拖放到其中)。

然后您就可以准确地了解正在发生什么,而不需要猜测和占卜。

您可以单击“调试托管代码”,查看进程在做什么。是否有任何等待或被阻止的异步任务?是否有任何可能通过HostedService取消了优雅的关闭的后台任务?那“并行堆栈”视图呢?有多少线程启动了?加载了哪些模块?

P.S. 是的,在回收后,IIS 通常会保留旧进程2-5分钟。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接