IIS 垃圾收集与许多应用程序域一起挂起

Posted

技术标签:

【中文标题】IIS 垃圾收集与许多应用程序域一起挂起【英文标题】:IIS Garbage Collection hangs with many appdomains 【发布时间】:2015-12-10 16:34:09 【问题描述】:

我们有一个 Asp.net 4.5 mvc webapi,它有大约 100 个应用程序域,每个都包含一个扩展。

现在我们有时会遇到 api 的问题。没有一条路由响应,即使是只返回字符串的状态 api 也没有响应。

当它挂起时,站点大约有 120 个线程(这很正常)和大约 12 GB RAM(异常高)。

当我们执行内存转储时,我们可以看到站点总是处于垃圾回收的中间。

大多数时候,我们看到大多数线程挂在堆栈中,代码处理应用程序域之间的序列化并等待 GC。 我们也有很多序列化,比如应用域通信和一些redis缓存的结合

等待约 5 分钟时的事件挂起未结束。是否存在与许多应用程序域相关的垃圾收集的任何已知问题?

由于站点托管在 IIS 中,因此后台 GC 应始终处于活动状态。

当我查看 GC 性能计数器中的时间时,我可以看到 GC 几乎总是在运行

我可以看到,当网站在 gc 中不断挂起 40% 的时间时

当站点处于这种状态时,我还可以看到内存在持续略微增加。

关于测试或尝试改进什么的任何提示?

将运行时升级到 4.5.2 是否可能会带来好处? 像这样:

ntdll!NtWaitForSingleObject+a 
KERNELBASE!WaitForSingleObjectEx+94 
clr!CLREventWaitHelper2+38 
clr!CLREventWaitHelper+1f 
clr!CLREventBase::WaitEx+70 
clr!SVR::gc_heap::wait_for_gc_done+55 
clr!SVR::WaitLonger+9e 
clr!SVR::GCHeap::Alloc+224 
clr!JIT_New+142 
[[HelperMethodFrame]] 
mscorlib_ni!System.Runtime.Serialization.ObjectManager.RegisterFixup(System.Runtime.Serialization.FixupHolder, Int64, Int64)+d1 
mscorlib_ni!System.Runtime.Serialization.Formatters.Binary.__BinaryParser.Run()+128 
mscorlib_ni!System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(System.Runtime.Remoting.Messaging.HeaderHandler, System.Runtime.Serialization.Formatters.Binary.__BinaryParser, Boolean, Boolean, System.Runtime.Remoting.Messaging.IMethodCallMessage)+db 
mscorlib_ni!System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(System.IO.Stream, System.Runtime.Remoting.Messaging.HeaderHandler, Boolean, Boolean, System.Runtime.Remoting.Messaging.IMethodCallMessage)+1bf 
mscorlib_ni!System.Runtime.Remoting.Channels.CrossAppDomainSerializer.DeserializeObject(System.IO.MemoryStream)+f8 
mscorlib_ni!System.Runtime.Remoting.Messaging.SmuggledMethodCallMessage.FixupForNewAppDomain()+de8a4e 
mscorlib_ni!System.Runtime.Remoting.Channels.CrossAppDomainSink.DoDispatch(Byte[], System.Runtime.Remoting.Messaging.SmuggledMethodCallMessage, System.Runtime.Remoting.Messaging.SmuggledMethodReturnMessage ByRef)+33 
mscorlib_ni!System.Runtime.Remoting.Channels.CrossAppDomainSink.DoTransitionDispatchCallback(System.Object[])+92 
clr!CallDescrWorkerInternal+83 
clr!CallDescrWorkerWithHandler+4a 
clr!DispatchCallDebuggerWrapper+1f 
clr!DispatchCallSimple+88 
clr!ThreadNative::InternalCrossContextCallback+2ea 
[[ContextTransitionFrame]] 
[[HelperMethodFrame_PROTECTOBJ] (System.Threading.Thread.InternalCrossContextCallback)] System.Threading.Thread.InternalCrossContextCallback(System.Runtime.Remoting.Contexts.Context, IntPtr, Int32, System.Threading.InternalCrossContextDelegate, System.Object[]) 
mscorlib_ni!System.Runtime.Remoting.Channels.CrossAppDomainSink.DoTransitionDispatch(Byte[], System.Runtime.Remoting.Messaging.SmuggledMethodCallMessage, System.Runtime.Remoting.Messaging.SmuggledMethodReturnMessage ByRef)+a0 
mscorlib_ni!System.Runtime.Remoting.Channels.CrossAppDomainSink.SyncProcessMessage(System.Runtime.Remoting.Messaging.IMessage)+15d 
mscorlib_ni!System.Runtime.Remoting.Proxies.RemotingProxy.CallProcessMessage(System.Runtime.Remoting.Messaging.IMessageSink, System.Runtime.Remoting.Messaging.IMessage, System.Runtime.Remoting.Contexts.ArrayWithSize, System.Threading.Thread, System.Runtime.Remoting.Contexts.Context, Boolean)+8c 
mscorlib_ni!System.Runtime.Remoting.Proxies.RemotingProxy.InternalInvoke(System.Runtime.Remoting.Messaging.IMethodCallMessage, Boolean, Int32)+22c 
mscorlib_ni!System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(System.Runtime.Remoting.Proxies.MessageData ByRef, Int32)+1f4 
clr!CTPMethodTable__CallTargetHelper3+12 
clr!CallTargetWorker2+74 
clr!CTPMethodTable::OnCall+1fb 
clr!TransparentProxyStub_CrossContextPatchLabel+a 
[[TPMethodFrame] (SR.BusPortal.Providers.Contract.Common.IAdapterSearcher.SearchAsync)] SR.BusPortal.Providers.Contract.Common.IAdapterSearcher.SearchAsync(SR.BusPortal.Providers.Contract.Common.AdapterSearchParameters) 
SR.BusPortal.Search.Steps.SearchStepOneWay`2+<SearchOneWayAsync>d__3[[System.__Canon, mscorlib],[System.__Canon, mscorlib]].MoveNext()+73 

【问题讨论】:

我相信每个 AppDomain 都应该是自己的沙箱。因此,一个 AppDomain 中的垃圾收集不应该冻结另一个。使用 RedGate 或 Jetbrain 的内存工具查看哪些对象正在占用内存。您会惊讶于 Web 应用程序的大对象堆中有多少对象。当您向另一个 Web 服务发出请求时,它通常是大字节数据数组。看看是否可以汇集任何资源。 【参考方案1】:

经过进一步调查,appdomains 不是原因。 我希望这可以为其他人节省大量搜索:-)

我们在 webapi 进程中有一个很大的内存 GraphDatabase(它使用了大约 30GB 的 RAM)。结果我们的webapi项目和同一个进程的graphdatabase出现了问题,GC从来没有成功结束这个进程。使用非异步 gc 问题会更好,但有时会有点滞后。

将此数据库分离到自己的服务后,此行为再也没有发生过。

还有很多关于如何为 GC 优化代码的帖子可能会有所帮助

【讨论】:

以上是关于IIS 垃圾收集与许多应用程序域一起挂起的主要内容,如果未能解决你的问题,请参考以下文章

垃圾收集时的本机线程行为

Redux + ImmutableJS - 如何垃圾收集太大的存储?

垃圾收集器什么时候回收垃圾?

JS高程4.变量,作用域和内存问题垃圾收集

垃圾收集块级作用域

iOS 上的 JavaScriptCore:VM 垃圾收集器不会自动清空