Microsoft.Web.Administration内存泄漏

有一个Windows服务,其中包括监视IIS应用程序池。 如果任何池已配置应用程序且未运行,则启动它(池)。 这已经有一段时间了。 最近发现该服务正在泄漏内存。 查看内存转储的罪魁祸首是用于检查应用程序池的Microsoft.Web.Administration。 唯一的一次性对象是ServerManager,我在一个使用块中。 已找到有关此泄漏的其他报告但尚无解决方案。 (请参阅http://msdn.microsoft.com/en-us/library/microsoft.web.administration.servermanager(v=vs.90).aspx#CommunityContent中的用户评论)

转储Microsoft.Web.Administration.ServerManager(此转储中的481)的所有根时,我只看到其中一个根。 假设这是当前的迭代。

不知道为什么这些Web.Administration对象无法收集,即使它们似乎没有根(?)。 有关如何堵塞这种泄漏的任何想法?

我将代码移动到控制台应用程序并在那里重现泄漏。

using System; using System.Collections.Generic; using System.Linq; using System.Threading; using Microsoft.Web.Administration; namespace Web.Administration.LeakExample { public static class ServerManagerExtensions { ///  /// Returns the application counts for application pools under the specified ServerManager. /// Application pools without applications will not be returned. ///  ///  ///  public static Dictionary GetPoolApplicationCounts(this ServerManager manager) { if (manager == null) { throw new ArgumentNullException(); } var appCounts = new Dictionary(manager.ApplicationPools.Count); foreach (var app in manager.Sites.SelectMany(site => site.Applications)) { if (!appCounts.ContainsKey(app.ApplicationPoolName)) { appCounts.Add(app.ApplicationPoolName, 1); } else { appCounts[app.ApplicationPoolName]++; } } return appCounts; } } class Program { static void Main(string[] args) { while (!Console.KeyAvailable) { Console.WriteLine("Checking App Pools..."); using (var manager = new ServerManager()) { var appCounts = manager.GetPoolApplicationCounts(); var appPools = manager.ApplicationPools.Where(pool => appCounts.ContainsKey(pool.Name)); foreach ( var pool in appPools.Where( pool => (pool.State != ObjectState.Started) && (pool.State != ObjectState.Starting))) { var state = pool.Start(); if ((state == ObjectState.Started) || (state == ObjectState.Starting)) { Console.WriteLine("Started app pool \"{0}\"", pool.Name); } else { Console.WriteLine("Failed to start app pool \"{0}\". state:{1}", pool.Name, state); } } } Console.WriteLine("Sleeping..."); Thread.Sleep(1000); } } } } 

WinDbg会话:

 > !dumpheap -stat 606b778c 479 5748 System.Runtime.Remoting.Activation.ConstructionLevelActivator 00472088 480 5760 System.Collections.Generic.Dictionary`2+ValueCollection[[System.String, mscorlib],[Microsoft.Web.Administration.Interop.IAppHostElement, Microsoft.Web.Administration]] 00471d5c 480 5760 System.Collections.Generic.SortedList`2+ValueList[[System.String, mscorlib],[Microsoft.Web.Administration.Configuration, Microsoft.Web.Administration]] 00194568 481 5772 Web.Administration.LeakExample.Program+c__DisplayClass3 00198438 480 7680 Microsoft.Web.Administration.Interop.AppHostWritableAdminManager 0047199c 481 7696 System.Linq.Enumerable+c__DisplayClassf`1[[Microsoft.Web.Administration.ApplicationPool, Microsoft.Web.Administration]] 606cc200 958 11496 System.Char 00471e2c 479 11496 System.Collections.Generic.SortedList`2+SortedListValueEnumerator[[System.String, mscorlib],[Microsoft.Web.Administration.Configuration, Microsoft.Web.Administration]] 0047090c 479 11496 System.Collections.Generic.List`1+Enumerator[[Microsoft.Web.Administration.Site, Microsoft.Web.Administration]] 004706f0 958 11496 Microsoft.Web.Administration.ConfigurationElementCollectionBase`1+c__DisplayClass4[[Microsoft.Web.Administration.Site, Microsoft.Web.Administration]] 00471aec 480 11520 System.Collections.Generic.List`1+Enumerator[[Microsoft.Web.Administration.ApplicationPool, Microsoft.Web.Administration]] 0047041c 480 11520 System.Collections.Generic.List`1[[Microsoft.Web.Administration.Site, Microsoft.Web.Administration]] 00196d58 480 11520 System.Collections.Generic.List`1[[Microsoft.Web.Administration.Configuration, Microsoft.Web.Administration]] 004715cc 481 11544 System.Collections.Generic.List`1[[Microsoft.Web.Administration.ApplicationPool, Microsoft.Web.Administration]] 0047103c 1437 17244 Microsoft.Web.Administration.ConfigurationElementCollectionBase`1+c__DisplayClass4[[Microsoft.Web.Administration.Application, Microsoft.Web.Administration]] 606ccfc8 1438 17256 System.Int32 00196a58 480 19200 System.Collections.Generic.SortedList`2[[System.String, mscorlib],[Microsoft.Web.Administration.Configuration, Microsoft.Web.Administration]] 0019660c 480 21120 Microsoft.Web.Administration.ConfigurationManager 00471130 958 22992 System.Collections.Generic.List`1+Enumerator[[Microsoft.Web.Administration.Application, Microsoft.Web.Administration]] 00470d68 960 23040 System.Collections.Generic.List`1[[Microsoft.Web.Administration.Application, Microsoft.Web.Administration]] 00198558 480 23040 System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[Microsoft.Web.Administration.Interop.IAppHostElement, Microsoft.Web.Administration]] 00196a08 481 23088 Microsoft.Web.Administration.Configuration 00183170 481 23088 System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.UInt32, mscorlib]] 606b7574 479 24908 System.Runtime.Remoting.Messaging.ConstructorCallMessage 00199e7c 479 24908 System.Linq.Enumerable+d__14`2[[Microsoft.Web.Administration.Site, Microsoft.Web.Administration],[Microsoft.Web.Administration.Application, Microsoft.Web.Administration]] 00199384 480 28800 System.Collections.Generic.Dictionary`2+Entry[[System.String, mscorlib],[Microsoft.Web.Administration.Interop.IAppHostElement, Microsoft.Web.Administration]][] 00470874 958 30656 System.Predicate`1[[Microsoft.Web.Administration.Site, Microsoft.Web.Administration]] 004718a8 962 30784 System.Linq.Enumerable+WhereEnumerableIterator`1[[Microsoft.Web.Administration.ApplicationPool, Microsoft.Web.Administration]] 001944d8 481 30784 Microsoft.Web.Administration.ServerManager 00195178 963 30816 System.Func`2[[Microsoft.Web.Administration.ApplicationPool, Microsoft.Web.Administration],[System.Boolean, mscorlib]] 606ccf90 968 31268 System.Int32[] 00197a50 480 32640 Microsoft.Web.Administration.SiteCollection 00194da0 481 32708 Microsoft.Web.Administration.ApplicationPoolCollection 004719f8 2874 34488 Microsoft.Web.Administration.ConfigurationElementCollectionBase`1+c__DisplayClass4[[Microsoft.Web.Administration.ApplicationPool, Microsoft.Web.Administration]] 0019810c 960 42240 Microsoft.Web.Administration.ConfigurationSection 00471098 1437 45984 System.Predicate`1[[Microsoft.Web.Administration.Application, Microsoft.Web.Administration]] 606cc1c8 961 46638 System.Char[] 001838d0 481 59644 System.Collections.Generic.Dictionary`2+Entry[[System.String, mscorlib],[System.UInt32, mscorlib]][] 606bedd0 5269 63228 System.UInt32 00470a7c 960 69120 Microsoft.Web.Administration.ApplicationCollection 00197c60 960 76800 Microsoft.Web.Administration.Site 00197ea8 1440 86400 Microsoft.Web.Administration.Application 00471a54 2874 91968 System.Predicate`1[[Microsoft.Web.Administration.ApplicationPool, Microsoft.Web.Administration]] 6067c684 4832 141272 System.Object[] 00195024 2886 196248 Microsoft.Web.Administration.ApplicationPool 606c7b20 16323 261168 System.__ComObject >.foreach (obj {!dumpheap -mt 001944d8 -short}){!gcroot -all obj} 002ef124 003f0359 Web.Administration.LeakExample.Program.Main(System.String[]) [c:\Users\Chuck\Documents\Visual Studio 2012\Projects\Web.Administration.LeakExample\Web.Administration.LeakExample\Program.cs @ 74] ebp+70: 002ef148 -> 02773270 System.Linq.Enumerable+WhereEnumerableIterator`1[[Microsoft.Web.Administration.ApplicationPool, Microsoft.Web.Administration]] -> 02772948 Microsoft.Web.Administration.ApplicationPoolCollection -> 02772060 Microsoft.Web.Administration.ServerManager 002ef124 003f0359 Web.Administration.LeakExample.Program.Main(System.String[]) [c:\Users\Chuck\Documents\Visual Studio 2012\Projects\Web.Administration.LeakExample\Web.Administration.LeakExample\Program.cs @ 74] ebp+68: 002ef150 -> 02773270 System.Linq.Enumerable+WhereEnumerableIterator`1[[Microsoft.Web.Administration.ApplicationPool, Microsoft.Web.Administration]] -> 02772948 Microsoft.Web.Administration.ApplicationPoolCollection -> 02772060 Microsoft.Web.Administration.ServerManager 002ef124 003f0359 Web.Administration.LeakExample.Program.Main(System.String[]) [c:\Users\Chuck\Documents\Visual Studio 2012\Projects\Web.Administration.LeakExample\Web.Administration.LeakExample\Program.cs @ 74] ebp+78: 002ef140 -> 027732c0 System.Linq.Enumerable+WhereEnumerableIterator`1[[Microsoft.Web.Administration.ApplicationPool, Microsoft.Web.Administration]] -> 02772948 Microsoft.Web.Administration.ApplicationPoolCollection -> 02772060 Microsoft.Web.Administration.ServerManager 002ef124 003f0359 Web.Administration.LeakExample.Program.Main(System.String[]) [c:\Users\Chuck\Documents\Visual Studio 2012\Projects\Web.Administration.LeakExample\Web.Administration.LeakExample\Program.cs @ 74] ebp+6c: 002ef14c -> 02773270 System.Linq.Enumerable+WhereEnumerableIterator`1[[Microsoft.Web.Administration.ApplicationPool, Microsoft.Web.Administration]] -> 02772948 Microsoft.Web.Administration.ApplicationPoolCollection -> 02772060 Microsoft.Web.Administration.ServerManager 002ef124 003f0359 Web.Administration.LeakExample.Program.Main(System.String[]) [c:\Users\Chuck\Documents\Visual Studio 2012\Projects\Web.Administration.LeakExample\Web.Administration.LeakExample\Program.cs @ 74] ebp+74: 002ef144 -> 027732c0 System.Linq.Enumerable+WhereEnumerableIterator`1[[Microsoft.Web.Administration.ApplicationPool, Microsoft.Web.Administration]] -> 02772948 Microsoft.Web.Administration.ApplicationPoolCollection -> 02772060 Microsoft.Web.Administration.ServerManager 002ef124 003f0359 Web.Administration.LeakExample.Program.Main(System.String[]) [c:\Users\Chuck\Documents\Visual Studio 2012\Projects\Web.Administration.LeakExample\Web.Administration.LeakExample\Program.cs @ 74] ebp+60: 002ef158 -> 02772948 Microsoft.Web.Administration.ApplicationPoolCollection -> 02772060 Microsoft.Web.Administration.ServerManager 002ef124 003f0359 Web.Administration.LeakExample.Program.Main(System.String[]) [c:\Users\Chuck\Documents\Visual Studio 2012\Projects\Web.Administration.LeakExample\Web.Administration.LeakExample\Program.cs @ 74] ebp+3c: 002ef17c -> 02772060 Microsoft.Web.Administration.ServerManager 002ef124 003f0359 Web.Administration.LeakExample.Program.Main(System.String[]) [c:\Users\Chuck\Documents\Visual Studio 2012\Projects\Web.Administration.LeakExample\Web.Administration.LeakExample\Program.cs @ 74] ebp+4c: 002ef16c -> 027732c0 System.Linq.Enumerable+WhereEnumerableIterator`1[[Microsoft.Web.Administration.ApplicationPool, Microsoft.Web.Administration]] -> 02772948 Microsoft.Web.Administration.ApplicationPoolCollection -> 02772060 Microsoft.Web.Administration.ServerManager 002ef124 003f0359 Web.Administration.LeakExample.Program.Main(System.String[]) [c:\Users\Chuck\Documents\Visual Studio 2012\Projects\Web.Administration.LeakExample\Web.Administration.LeakExample\Program.cs @ 74] ebp+40: 002ef178 -> 02773270 System.Linq.Enumerable+WhereEnumerableIterator`1[[Microsoft.Web.Administration.ApplicationPool, Microsoft.Web.Administration]] -> 02772948 Microsoft.Web.Administration.ApplicationPoolCollection -> 02772060 Microsoft.Web.Administration.ServerManager 002ef124 003f0359 Web.Administration.LeakExample.Program.Main(System.String[]) [c:\Users\Chuck\Documents\Visual Studio 2012\Projects\Web.Administration.LeakExample\Web.Administration.LeakExample\Program.cs @ 74] ebp+50: 002ef168 -> 02772060 Microsoft.Web.Administration.ServerManager Found 10 roots. 

当我第一次阅读这个问题时,我遇到的第一件事就是可以通过使用Windows激活服务(WAS)完全避免整个问题。

我第一次浏览你发布的代码让我觉得你的问题可能与访问/修改foreach循环创建的各种闭包有关。

尝试这些小的重构,看看是否有任何/所有能够减少或消除问题或失败,提供更多关于根本原因的信息:

  1. 通过立即调用.ToList()避免延迟执行LINQ查询
  2. 而不是重新使用ServerManager对象,在循环的每次迭代中创建一个新对象
  3. 由于您只使用ApplicationPool.Name ,因此重构:

    foreach (var app in manager.Sites.SelectMany(site => site.Applications))

对此:

 foreach (var app in manager.Sites.SelectMany(site => site.Applications, (s, a) => a.Name) .ToList()) 

我的假设是这就是发生的事情:

  • using语句中创建了ServerManager新实例
  • 在该对象引用上调用静态扩展方法,该方法在内部使用ApplicationPools
  • ApplicationPools会立即再次用于构造IQueryable,它可以立即使用(但不完全执行 – foreach语句在内部使用yield return )并进行过滤。
  • 调用.Start()更改(变异) ServerManager对象引用引用的ApplicationPool状态
  • 其余的不相关,所以循环回到开头。 它仍然使用相同的ServerManager 。 由于该引用具有变异状态(因为调用.Start() ),它可能被GC标记并且从未收集 – 在后续迭代的结果中永远不会返回该应用程序池,并且可能导致引用的孤立记忆。

这只是一个假设,请记住!

我现在相信这不是真正的泄漏。 正如Josh建议的那样,我尝试了ToList()。 这不会影响内存使用情况。

我在循环中添加了一个GC.Collect()(仅用于测试目的),并消除了“泄漏”。 我通过内存转储validation了这一点,我发现堆上没有Web.Application对象。 这恰好与我在之前的转储中看到的所有Web.Application对象中的所有对象实际上都是根的一致。 其他人没有扎根,等待收集。

我被.NET垃圾收集器的非确定性特性绊倒了。 对不起噪音。

我在这里找到了解决方案: http://www.tomanderson.me

建议使用

 using (ServerManager sm = ServerManager.OpenRemote("localhost")) { /*..*/ } 

对我有用。