调用.NET Serviced Component引发的性能问题及其解决

 

在企业用户环境里,.NET Serviced Component使用广泛。它比较好的把传统COM+封装和.NET应用逻辑衔接了起来,在服务器端应用起到重要作用。.NET Serviced Component 的使用需要注意到很多方面,特别是要做到对象资源合理应用(pooling)和及时释放(Dispose)。现有文章就这方面有很多具体讨论

在这里,我要分析的是一种以前处理过的特定情况下.NET Serviced Component的引发的High CPU的问题。不只一个客户遇到此类问题,觉得有必要和大家分享一下。

如果程序出现High CPU,这个应用程序线程一定出现了繁忙的运算,比如重复的计算或者长时间的循环,而不是等待数据库,锁,或其它资源。如果这个程序是.NET程序,频繁Garbage Collection导致High CPU就必须要重点考虑。因为每次GC都需要检查.NET 对象树和调整.NET堆, 是繁重的操作。我们可以通过捕捉性能日志(perfmon),查看.NET CLR Memory 对象下的%Time in GC 和 Induced GC 这两个性能计数器指标,来判断High CPU是不是和GC有关。

在这个例子里面,通过查看性能监视器,可以看到Induced GC增长非常的快速,表明是这次High CPU的直接原因:

 

 

通常情况下,Induced GC 出现是客户代码里面直接调用了GC.Collect 。然而,在查看客户代码后,发现根本没有GC.Collect出现。

我们注意到这个程序里面使用了.NET Enterprise Serviced Components, 开始便建议客户使用Dispose方法释放这些Components, 而不是用DisposeObject:

http://msdn.microsoft.com/en-us/library/system.enterpriseservices.servicedcomponent.disposeobject.aspx

并且尝试启用对象池,, 发现改善并不明显:

http://www.codeproject.com/Articles/579/COM-Object-Pooling

http://msdn.microsoft.com/en-us/library/windows/desktop/ms684273(v=vs.85).aspx

于是在这个程序High CPU的时候抓取了Memory Dump。

注:抓取Memory Dump的方法很多,可以用WinDBG, procdump或者DebugDiag

通过仔细分析Memory Dump,发现Induced GC逻辑出现在以下Call Stack里面:

非托管 Call Stack:

Child-SP          RetAddr           Call Site
00000000`0f59bbe8 00000000`7704ce00 ntdll!ZwWaitForSingleObject+0xa
00000000`0f59bbf0 000007fe`f52fa74a kernel32!WaitForSingleObjectEx+0x9c
00000000`0f59bcb0 000007fe`f52fa83b mscorwks!CLREventWaitHelper+0x42
00000000`0f59bd10 000007fe`f53cee38 mscorwks!CLREvent::WaitEx+0x63
00000000`0f59bdc0 000007fe`f53c1cb7 mscorwks!SVR::gc_heap::wait_for_gc_done+0x88
00000000`0f59be00 000007fe`f542913b mscorwks!SVR::GCHeap::GarbageCollectGeneration+0x147
00000000`0f59be90 000007fe`f5412ac3 mscorwks!SVR::GCHeap::GarbageCollect+0x5b
00000000`0f59bee0 000007fe`f57301a5 mscorwks!GCInterface::AddMemoryPressure+0x13b
00000000`0f59bf70 000007fe`f57b9beb mscorwks!RCW::AddMemoryPressure+0x15
00000000`0f59bfb0 000007fe`f57dbbf4 mscorwks!RCW::CreateRCW+0x17b
00000000`0f59c030 000007fe`f57dbdea mscorwks!COMInterfaceMarshaler::CreateObjectRef+0x74
00000000`0f59c130 000007fe`f58843f4 mscorwks!COMInterfaceMarshaler::WrapWithComObject+0x3a
00000000`0f59c1a0 000007fe`f0268995 mscorwks!MarshalNative::WrapIUnknownWithComObject+0x134
00000000`0f59c3c0 000007fe`f0270933 System_EnterpriseServices_ni!System.EnterpriseServices.RemoteServicedComponentProxy..ctor(System.Type, IntPtr, Boolean)+0xd5
00000000`0f59c420 000007fe`f0270213 System_EnterpriseServices_ni!System.EnterpriseServices.FastRSCPObjRef.GetRealObject(System.Runtime.Serialization.StreamingContext)+0x33
00000000`0f59c470 000007fe`f37254db System_EnterpriseServices_ni!System.EnterpriseServices.ServicedComponentProxyAttribute.CreateProxy(System.Runtime.Remoting.ObjRef, System.Type, System.Object, System.Runtime.Remoting.Contexts.Context)+0x133
00000000`0f59c500 000007fe`f37253ff mscorlib_ni!System.Runtime.Remoting.RemotingServices.SetOrCreateProxy(System.Runtime.Remoting.Identity, System.Type, System.Object)+0x9b
00000000`0f59c560 000007fe`f372466f mscorlib_ni!System.Runtime.Remoting.RemotingServices.GetOrCreateProxy(System.Runtime.Remoting.Identity, System.Object, Boolean)+0xbf
00000000`0f59c5c0 000007fe`f027050a mscorlib_ni!System.Runtime.Remoting.RemotingServices.InternalUnmarshal(System.Runtime.Remoting.ObjRef, System.Object, Boolean)+0x12f
00000000`0f59c650 000007fe`f40e3edf System_EnterpriseServices_ni!System.EnterpriseServices.ServicedComponentProxyAttribute.CreateInstance(System.Type)+0x2ba
00000000`0f59c7c0 000007fe`f5416e61 mscorlib_ni!System.Runtime.Remoting.Activation.ActivationServices.IsCurrentContextOK(System.Type, System.Object[], Boolean)+0x9ba81f

 

对应的.NET 托管 Call Stack:

Child-SP         RetAddr          Call Site

000000000f59c3c0 000007fef0270933 System.EnterpriseServices.RemoteServicedComponentProxy..ctor(System.Type, IntPtr, Boolean)
000000000f59c420 000007fef0270213 System.EnterpriseServices.FastRSCPObjRef.GetRealObject(System.Runtime.Serialization.StreamingContext)
000000000f59c470 000007fef37254db System.EnterpriseServices.ServicedComponentProxyAttribute.CreateProxy(System.Runtime.Remoting.ObjRef, System.Type, System.Object, System.Runtime.Remoting.Contexts.Context)
000000000f59c500 000007fef37253ff System.Runtime.Remoting.RemotingServices.SetOrCreateProxy(System.Runtime.Remoting.Identity, System.Type, System.Object)
000000000f59c560 000007fef372466f System.Runtime.Remoting.RemotingServices.GetOrCreateProxy(System.Runtime.Remoting.Identity, System.Object, Boolean)
000000000f59c5c0 000007fef027050a System.Runtime.Remoting.RemotingServices.InternalUnmarshal(System.Runtime.Remoting.ObjRef, System.Object, Boolean)
000000000f59c650 000007fef40e3edf System.EnterpriseServices.ServicedComponentProxyAttribute.CreateInstance(System.Type)

通过上面的调用栈信息,可以得知当serviced componnet被创建的时候,.NET RCW 逻辑会在mscorwks!RCW::CreateRCW  里调用GC.AddMemoryPressure 对可能的额外内存需要做提前预估. 当内存在不停扩展时, AddMemoryPressure会增加 Induced GC 的计数(如我们在性能监视器里发现的), 并且调用GarbageCollect。

为了解决这种问题,我们需要在应用程序里减少创建Serviced Component的次数。光在COM+里面修改成Object Pooling是不够的,因为这种Induced GC是在每次应用程序调用RCW的时候都会发生。通过研究代码和测试,如在每次调用创建Serviced Component之前使用 RemoveMemoryPressure(4004) 用来抵消RCW里的相同bytes的AddMemoryPressure(4004)可以临时解决这个问题.

最终解决方法是我们在应用程序本身创建了自己的对象池(建立一组全局对象,简单管理它们的存在周期),以彻底减少CreateInsance和CreateRCW的调用。这样处理以后,即使在大压力情况下, 程序运行也是良好的。

时间: 2024-10-14 11:12:44

调用.NET Serviced Component引发的性能问题及其解决的相关文章

和表值函数连接引发的性能问题

原文:和表值函数连接引发的性能问题     最近调优过程中遇到一个问题,就是表值函数作为连接中的一部分时,可能会引起麻烦,本文会简单阐述表值函数是什么,以及为什么使用表值函数进行连接时会引发性能问题. 表值函数     SQL Server中提供了类似其他编程语言的函数,而函数的本质通常是一段代码的封装,并返回值.在SQL Server中,函数除了可以返回简单的数据类型之外(Int.Varchar等),还可以返回一个集合,也就是返回一个表.     而根据是否直接返回集合或是定义后再返回集合,表

Mysql中where条件一个单引号引发的性能损耗

日常写SQL中可能会有一些小细节忽略了导致整个sql的性能下降了好几倍甚至几十倍,几百倍.以下这个示例就是mysql语句中的一个单引号('')引发的性能耗损,我相信很多朋友都遇到过,甚至还在这样写. 先看下我的表结构: CREATE TABLE `d_sku` ( `id` varchar(36) NOT NULL, `commodity_id` varchar(36) DEFAULT NULL, `counts` int(11) DEFAULT NULL, `price` double(15,

水晶报表 Crystal Report 调用存储过程时出错 找不到表 ,解决方法。

用 CrystalReportViewer1 控件在asp.net的网页上显示报表,如果做报表时调用数据表数据的方式调用是可以成功的,但报表是用存储过程获取数据方式会出现以下错误: 找不到表'RptOpenCheck;1' . 文件 G:\TEMP\FO-OpenCheck {6D191F06-DECF-4A25-88FC-8553E3D435AA}.rpt 内出错: 找不到表. Error: 未将对象引用设置到对象的实例. The table 'RptOpenCheck;1' could no

ArcGIS API for Silverlight 调用WebService出现跨域访问报错的解决方法

原文:ArcGIS API for Silverlight 调用WebService出现跨域访问报错的解决方法 群里好几个朋友都提到过这样的问题,说他们在Silverlight中调用了WebService方法,总报这个错误,贴图如下: 解决办法: 1.确定你的Silverlight项目及承载Silverlight的Web程序根目录下都包含2个跨域文件,分别是crossdomain.xml和clientaccesspolicy.xml: crossdomain.xml文件,如下: <?xml ve

java 调用bash shell脚本阻塞的小问题的解决

java  调用bash shell脚本阻塞的小问题的解决 背景 使用java实现的web端,web端相应用户的界面操作,使用java调用bash实现的shell脚本进行实际的操作,操作完成返回执行结果给web 界面显示. 现象: Java 进程阻塞.使用ps命令可以看到被调用的shell 的执行进程状态为S 分析 Shell子进程的状态是S 睡眠状态,也就是该进程在等待某个条件满足,方能继续执行. Java程序在调用Runtime.getRuntime().exec(jyName)之后,lin

小程序云开发调用HTTP请求中got第三方库使用失败解决方法

小程序云开发调用HTTP请求中got第三方库使用失败解决方法 错误代码 {"errorCode":1,"errorMessage":"user code exception caught","stackTrace":"The \"original\" argument must be of type function"} 替换方案 // 云函数入口文件 const cloud = req

近期业务需要所引发的性能优化问题,浅谈线程池性能优化

线程池对于性能优化无处不在 1.楼主在平时产品开发过程中所遇到的性能问题,特别是最近特别流行的微服务架构. web - java - 底层数据源(python亦或者opensatck),对于这种前后台分离的场景 无时无刻会存在对于业务场景需要对同一数据源进行百次,千次的重复调用过程. 性能方面就会出现接口延迟,过慢,超时等情况 下面就楼主最近遇到的一个业务场景加以举例说明:做云计算相关的想必都是知道,我们在为客户提供单板的过程中(也就是物理机):会对众多的单板进行管理,这里我们就引进主机组: 主

一个SQL引发的性能问题

一个性能测试项目,一个值得深思的问题. 1.症状: 12小时稳定性压测,压力恒定 系统响应时间随压测时间的增长而不断增大 如图所示: 2.性能分析,性能问题定位 1)         系统负载检查:在压测过程中,压力恒定,CPU 利用率没有较大的波幅 2)         磁盘IO检查:在压测过程中,磁盘读写稳定,没有较大的请求等待队列 3)         网络检查:在压测过程中,无较大的数据传输,网络延迟正常,测试环境的网络正常. 4)         内存检查:系统可用内存逐渐减小,JVM

java调用windows的wmi获取设备性能数据

java调用windows的wmi获取监控数据(100%纯java调用windows的wmi获取监控数据) 转:http://my.oschina.net/noahxiao/blog/73163 纯java主要体现在可以跨平台调用com.所用的是j-Interop,它是100%纯java实现的com的调用 1.环境准备 a.windows要开启Remote Registry与Windows Management Instrumentation服务 b.修改安全策略 我系统是英文的,如果是中文系统