gpt4 book ai didi

c# - 为什么在调用 ServiceHost.Open 之前实例化 XmlSerializer 会产生内存和句柄泄漏

转载 作者:太空狗 更新时间:2023-10-29 18:20:26 26 4
gpt4 key购买 nike

在 .NET/WCF/Windows 服务中寻找内存和句柄泄漏时,我注意到了我无法解释的奇怪行为。这里是设置和分辨率。我正在寻找的是对观察到的行为的解释。

我安装了 Windows 服务。
我开始服务了。
我使用事务性 WCF 调用调用了一个简单方法(每次调用新 channel - 无缓存)。
对于每次调用,大约有 2 个句柄保留在内存中。

如果以下项目适用,则可以观察到这一点:

  1. 它是一个 Windows 服务;不要将其作为控制台应用程序运行。
  2. 使用事务(单独的进程或仅经过机器测试)调用 WCF 方法。
  3. 在调用 ServiceBase.Run(servicesToRun); 之前用某种类型实例化 XmlSerializer。
  4. 类型是自定义类型。它不会出现在 new XmlSerializer(typeof(string)) 或 new XmlSerializer(typeof(XmlDocument)) 中。不需要调用序列化。如果自定义类型只有一个字符串作为属性就足够了(任何地方都没有句柄!)
  5. 使用 SGen.exe 创建静态 XmlSerialization.dll 不会产生此问题。

我的代码已经包含修复:
最早在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();
}
)
}

有人可以解释这种行为吗?

请注意,我对找到一种避免泄漏的方法不感兴趣(我已经知道如何做到这一点),而是对解释(即为什么会发生这种情况)感兴趣。

最佳答案

我认为一些内部工作原理可以公正地回答这个问题。我从脑后开始这样做,因为前段时间我也遇到了这个问题,我花了一天时间来追溯,包括广泛使用 Reflector 和 ANTS Memory profiler(在我以前的公司)......在这里去:

XML Serializer 内部所做的是使用 System.Reflection.Emit 创建一个类(我们称之为“A”),该类接受您传递给它的类型。相对而言,构建这样一个类会花费很多时间,并且由于类型不会改变,因此可以重用。因此,构造的类型存储在字典中,例如它以一些 Dictionary 结束。

对于已知(基本)类型,序列化程序代码是固定的,例如无论您重新启动应用程序多少次,字符串的序列化都不会改变。请注意与“A”的区别,其中任何类型在首次传递给 XMLSerializer 之前对于序列化工厂都是未知的。

XMLSerializer 第一次使用该类型时,您传递的类型和它需要的所有类型(例如,需要序列化的所有字段和属性)都会发生此过程。

关于泄漏...当您调用 ChannelFactory 时,如果尚不存在,它会构造序列化程序。为此,它检查序列化程序是否已存在于字典中,如果不存在,则通过创建 ISomeSerializerType 的实例来创建一个。

由于一些愚蠢的原因,工厂中存在一个错误,它构造了一个新的序列化程序而不将其存储在字典中。构造完成后,您最终会得到一个新类型——它显示为泄漏(记住:类型永远无法卸载)——即使对象已正确处置。当您首先使用 XMLSerializer 或创建静态类时,它会正确使用 Dictionary 缓存,这意味着它不会泄漏。所以你明白了,这是一个错误。我曾经访问过 ANTS Memory Profiler,它很好地展示了这一点。

希望这能解释。

关于c# - 为什么在调用 ServiceHost.Open 之前实例化 XmlSerializer 会产生内存和句柄泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13086015/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com