【发布时间】:2014-12-01 22:34:06
【问题描述】:
我注意到 WCF 应用程序中存在内存泄漏问题,并设法在一个简单的程序中复制了它。从另一个 WCF 服务中调用 WCF 服务时会出现此问题。
在以下示例中,我有两个服务A 和B。当我在服务A 上调用DoWork 方法时,它又调用DoWork 服务B 的方法。
在下面的示例中,我每次都创建一个新的ChannelFactory,使用它打开一个通道,调用DoWork,然后在最后处理通道和工厂。这样,进程就会开始泄漏内存。
如果我将其中一个调用或两者都设置为每次重用相同的ChannelFactory(注释和取消注释示例中的标记行),泄漏就会停止。
如果我每次调用服务A 时都继续创建一个新的ChannelFactory,但将ServiceA 的DoWork 方法置空(因此它不会调用ServiceB),则不会发生泄漏。
我正在运行针对 .NET 3.5 的程序。奇怪的是,如果我切换到 .NET 4、4.5 或 4.5.1,进程会更快地泄漏内存。
谁能理解为什么会发生这种情况以及如何解决它(或至少解决它)?
示例代码如下:
using System;
using System.ServiceModel;
namespace memoryleak
{
internal class Program
{
private static void Main()
{
using (var hostA = new ServiceHost(new ServiceA(), new Uri("net.pipe://localhost")))
using (var hostB = new ServiceHost(new ServiceB(), new Uri("net.pipe://localhost")))
{
hostA.AddServiceEndpoint(typeof (ContractA), new NetNamedPipeBinding(), "test_service_a");
hostA.Open();
hostB.AddServiceEndpoint(typeof (ContractB), new NetNamedPipeBinding(), "test_service_b");
hostB.Open();
while(true)dowork();
}
}
//CALLING SERVICE A
//uncomment the following line to reuse the same ChannelFactory each time
//private static readonly ChannelFactory<ContractA> pipeFactory=new ChannelFactory<ContractA>(new NetNamedPipeBinding(),new EndpointAddress("net.pipe://localhost/test_service_a"));
private static void dowork()
{
//comment the following line to reuse the same ChannelFactory each time
var pipeFactory = new ChannelFactory<ContractA>(new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/test_service_a"));
ContractA provider = null;
try
{
provider = pipeFactory.CreateChannel();
provider.DoWork();
}
catch
{
}
finally
{
CloseChannel(provider);
//comment the following line to reuse the same ChannelFactory each time
try { pipeFactory.Close(); }catch{pipeFactory.Abort();}
}
}
private static void CloseChannel(ContractA provider)
{
try
{
if (provider == null)
return;
try
{
((IClientChannel) provider).Close();
}
catch
{
((IClientChannel) provider).Abort();
}
((IDisposable) provider).Dispose();
}
catch (Exception ex)
{
throw new Exception("Error while closing channel", ex);
}
}
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class ServiceA : ContractA
{
//CALLING SERVICE B
//uncomment the following line to reuse the same ChannelFactory each time
//private readonly ChannelFactory<ContractB> pipeFactory=new ChannelFactory<ContractB>(new NetNamedPipeBinding(),new EndpointAddress("net.pipe://localhost/test_service_b"));
public void DoWork()
{
//comment the following line to reuse the same ChannelFactory each time
var pipeFactory=new ChannelFactory<ContractB>(new NetNamedPipeBinding(),new EndpointAddress("net.pipe://localhost/test_service_b"));
ContractB provider = null;
try
{
provider = pipeFactory.CreateChannel();
provider.DoWork();
}
catch
{
}
finally
{
CloseChannel(provider);
//comment the following line to reuse the same ChannelFactory each time
try { pipeFactory.Close(); } catch { pipeFactory.Abort(); }
}
}
private void CloseChannel(ContractB provider)
{
try
{
if (provider == null)
return;
try
{
((IClientChannel) provider).Close();
}
catch
{
((IClientChannel) provider).Abort();
}
((IDisposable) provider).Dispose();
}
catch (Exception ex)
{
throw new Exception("Error while closing channel", ex);
}
}
}
[ServiceContract]
public interface ContractA
{
[OperationContract]
void DoWork();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class ServiceB : ContractB
{
public void DoWork()
{
}
}
[ServiceContract]
public interface ContractB
{
[OperationContract]
void DoWork();
}
}
【问题讨论】:
-
catch { }真的吗?如果你做这样的事情,你至少应该说明你为什么这样做。 -
简单...因为此代码仅用于说明问题...不是生产代码:)
-
为什么你认为你有内存泄漏?您使用什么工具来做出决定?
-
只需打开任务管理器即可显示该进程的内存使用率上升。离开了一段时间,它甚至最终使用了千兆字节的内存。
-
你把
private static void dowork()中的provider.DoWork();注释掉会怎样,即只是打开和关闭频道?
标签: c# .net wcf memory-leaks