在.NET / WCF / Windows服务中查找内存和句柄漏洞时,我发现了一些我无法解释的奇怪行为。 这里的设置和分辨率。我正在寻找的是对观察到的行为的解释。
我安装了Windows服务。
我开始服务了。
我用事务性WCF调用调用了一个简单的方法(每个调用的新通道 - 没有缓存)。
对于每次调用,大约有2个句柄留在内存中。
如果以下项目适用,则可以观察到:
ServiceBase.Run(servicesToRun);
实例化某种类型的XmlSerializer之前。new XmlSerializer(typeof(string))
或新XmlSerializer(typeof(XmlDocument))
不会发生这种情况。不需要调用序列化。如果自定义类型只有一个字符串作为属性(没有任何句柄!)我的代码已包含修复:
在OnStart()中使用XmlSerializer 最早:
Program.cs的
WindowsService winSvc = new WindowsService();
ServiceBase[] servicesToRun = new ServiceBase[]{winSvc};
ServiceBase.Run(servicesToRun);
WindowsService.cs
internal sealed class WindowsService : ServiceBase
{
private ServiceHost wcfServiceHost = null;
internal WindowsService()
{
AutoLog = true;
CanStop = true;
CanShutdown = true;
CanPauseAndContinue = false;
}
internal void StartWcfService()
{
wcfServiceHost = new ServiceHost(typeof(DemoService));
wcfServiceHost.Open();
}
protected override void Dispose(bool disposing)
{
if (wcfServiceHost != null)
{
wcfServiceHost.Close();
}
base.Dispose(disposing);
}
protected override void OnStart(string[] args)
{
new XmlSerializer(typeof(MyType));
StartWcfService();
}
}
DemoService.cs
[ServiceBehavior
(
InstanceContextMode = InstanceContextMode.PerSession,
TransactionAutoCompleteOnSessionClose = false,
IncludeExceptionDetailInFaults = true
)
]
public sealed class DemoService : IDemoService
{
[TransactionFlow(TransactionFlowOption.Allowed)]
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
public int Add(int a, int b)
{
return a + b;
}
}
Client.cs :
IChannelFactory<IDemoService> channelFactory = new ChannelFactory<IDemoService>("defaultClientConfiguration");
IDisposable channel = null;
for (int index = 0; index < 5000; index++)
{
using
(
channel = (IDisposable)channelFactory.CreateChannel(new EndpointAddress("net.tcp://localhost:23456/DemoService")))
{
IDemoService demoService = (IDemoService)channel;
using (TransactionScope tx = new TransactionScope(TransactionScopeOption.RequiresNew))
{
demoService.Add(3, 9);
tx.Complete();
}
)
}
有人可以解释这种行为吗?
请注意,我不想找到避免泄漏的方法(我已经知道如何做到这一点)但是在解释中(即为什么会发生这种情况)。
答案 0 :(得分:7)
我认为一些内部运作这个问题是正义的。我从头脑中做到这一点,因为我前段时间遇到了这个问题,我花了一天的时间来追踪,包括大量使用Reflector和ANTS Memory Profiler(在我以前的公司)......这里云:
XML Serializer在内部做的是使用System.Reflection.Emit创建一个类(让我们称之为'A'),它接受传递给它的类型。构建这样的类相对来说花费了大量时间,并且可以重复使用,因为类型不会改变。因此,构造的类型存储在字典中,例如,它以一些字典结束。
对于已知(基本)类型,序列化器代码是固定的,例如无论重启应用程序多少次,字符串的序列化都不会改变。请注意与“A”的区别,其中序列化工厂未知的任何类型,直到它首次传递给XMLSerializer。
XMLSerializer第一次使用该类型时,会对您传递的类型及其所需的所有类型(例如,需要序列化的所有字段和属性)执行此过程。
关于泄漏......当你调用ChannelFactory时,如果它还不存在,它会构造序列化器。为此,它检查字典中是否已存在序列化程序,如果不存在,则通过创建ISomeSerializerType实例来创建一个。
出于某些愚蠢的原因,工厂中存在构造新序列化程序而不将其存储在字典中的错误。构建完成后,最终会出现一个新类型 - 显示为泄漏(请记住:类型永远不会被卸载) - 即使对象被正确处理。首先使用XMLSerializer或创建静态类时,它会正确使用Dictionary缓存,这意味着它不会泄漏。所以,你有它,这是一个错误。我曾经访问过ANTS Memory Profiler,它很好地展示了这一点。
希望这能解释。
答案 1 :(得分:6)
XmlSerializer文档说明了这一点:
为了提高性能,XML序列化基础结构动态生成程序集以序列化和反序列化指定的类型。基础结构查找并重用这些程序集。仅当使用以下构造函数时才会出现此问题:
XmlSerializer.XmlSerializer(类型):
XmlSerializer.XmlSerializer(Type,String)
如果使用任何其他构造函数,则会生成同一程序集的多个版本,并且永远不会卸载,这会导致内存泄漏和性能下降。最简单的解决方案是使用前面提到的两个构造函数之一。否则,必须将程序集缓存在Hashtable中,如以下示例所示。
http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx