BuildManager 中的 WebAPI 内存泄漏

WebAPI memory leak in BuildManager

我有一个预编译的网络应用程序(32 位),其中内存使用量缓慢上升,直到出现 OutOfMemoryExceptions。使用探查器,我观察到主要嫌疑人是 System.Web.VirtualPath 对象中的字符串,这些字符串又存储在 System.Web.Compilation.BuildManager 对象的 _localResourcesAssemblies 哈希表中。

这些条目似乎以大约 50 MB 的批次添加,每 4-6 小时一次。我被卡住了——我不知道是什么调用了添加这些条目的 BuildManager。查看条目,它们包含匹配属性路由控制器(或有效路由路径的父路径)的有效路由的路径。

应用程序目录中不应更改任何文件。

我用类似的路由设置了一个简单的网络应用程序,看看是否可以重现该问题,但我无法在测试应用程序上重现该问题。

关于如何找到调用 BuildManager(密封 class,可能是单例)对象的任何想法?

解决方法:删除 Microsoft.AspNet.WebPages nuget 包(以及依赖它的页面,包括 Microsoft.AspNet.WebApi.HelpPage 包)。

我们终于得到了来自问题真正原因的内存不足的堆栈跟踪(我们的大部分跟踪都在我们自己的代码中,我们一次分配了几个 MB)。堆栈跟踪贴在下面。

我们能够发现 WebPages HTTP 模块被动态添加并注册回调,该回调将每个 url 路径添加到 BuildManager 中的缓存,并且由于我们的 32 位内存 space以及大量的独特路径,这最终会 运行 我们内存不足。

正如所承诺的,最终能够引导我们找到问题原因的堆栈跟踪:

Exception type: OutOfMemoryException 
Exception message: Exception of type 'System.OutOfMemoryException' was thrown.
 at System.Collections.Hashtable.rehash(Int32 newsize, Boolean forceNewHashCode)
 at System.Collections.Hashtable.expand()
 at System.Collections.Hashtable.Insert(Object key, Object nvalue, Boolean add)
 at System.Collections.Hashtable.set_Item(Object key, Object value)
 at System.Web.Compilation.BuildManager.EnsureFirstTimeDirectoryInit(VirtualPath virtualDir)
 at System.Web.Compilation.BuildManager.GetBuildResultFromCacheInternal(String cacheKey, Boolean keyFromVPP, VirtualPath virtualPath, Int64 hashCode, Boolean ensureIsUpToDate)
 at System.Web.Compilation.BuildManager.GetVPathBuildResultFromCacheInternal(VirtualPath virtualPath, Boolean ensureIsUpToDate)
 at System.Web.Compilation.BuildManager.GetVPathBuildResultInternal(VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile, Boolean throwIfNotFound, Boolean ensureIsUpToDate)
 at System.Web.Compilation.BuildManager.GetVPathBuildResultWithNoAssert(HttpContext context, VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile, Boolean throwIfNotFound, Boolean ensureIsUpToDate)
 at System.Web.Compilation.BuildManager.GetVirtualPathObjectFactory(VirtualPath virtualPath, HttpContext context, Boolean allowCrossApp, Boolean throwIfNotFound)
 at System.Web.Compilation.BuildManager.GetObjectFactory(String virtualPath, Boolean throwIfNotFound)
 at System.Web.WebPages.BuildManagerWrapper.GetObjectFactory(String virtualPath)
 at System.Web.WebPages.BuildManagerWrapper.ExistsInPrecompiledSite(String virtualPath)
 at System.Web.WebPages.BuildManagerWrapper.Exists(String virtualPath)
 at System.Web.WebPages.VirtualPathFactoryManager.Exists(String virtualPath)
 at System.Web.WebPages.DefaultDisplayMode.GetDisplayInfo(HttpContextBase httpContext, String virtualPath, Func`2 virtualPathExists)
 at System.Web.WebPages.DisplayModeProvider.GetDisplayInfoForVirtualPath(String virtualPath, HttpContextBase httpContext, Func`2 virtualPathExists, IDisplayMode currentDisplayMode, Boolean requireConsistentDisplayMode)
 at System.Web.WebPages.DisplayModeProvider.GetDisplayInfoForVirtualPath(String virtualPath, HttpContextBase httpContext, Func`2 virtualPathExists, IDisplayMode currentDisplayMode)
 at System.Web.WebPages.WebPageRoute.GetRouteLevelMatch(String pathValue, String[] supportedExtensions, Func`2 virtualPathExists, HttpContextBase context, DisplayModeProvider displayModeProvider)
 at System.Web.WebPages.WebPageRoute.MatchDefaultFiles(String pathValue, String[] supportedExtensions, Func`2 virtualPathExists, HttpContextBase context, DisplayModeProvider displayModes, String currentLevel)
 at System.Web.WebPages.WebPageRoute.MatchRequest(String pathValue, String[] supportedExtensions, Func`2 virtualPathExists, HttpContextBase context, DisplayModeProvider displayModes)
 at System.Web.WebPages.WebPageRoute.DoPostResolveRequestCache(HttpContextBase context)
 at System.Web.WebPages.WebPageHttpModule.OnApplicationPostResolveRequestCache(Object sender, EventArgs e)
 at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
 at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)