微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

c# – 从另一个WCF服务中调用WCF服务时WCF内存泄漏

我注意到WCF应用程序中存在内存泄漏问题,并设法在一个简单的程序中复制它.从另一个WCF服务中调用WCF服务时会出现此问题.

在下面的示例中,我有两个服务A和B.当我在服务A上调用DoWork方法时,它又调用服务B的DoWork方法.

在下面的示例中,我每次都创建一个新的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),"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();
    }
}

解决方法

可能发生的是,您创建的对象比认工作站垃圾收集器可以摆脱它们更快. .net 3.5 GC的认延迟模式是交互式的,这意味着如果为了保持UI响应而收集时间过长则会放弃. GC在.net 4& 4.5这可能是你看到不同增长率的原因.

尝试在App.config中打开服务器垃圾回收模式,看看行为是否发生了变化.这应该允许GC工作直到完成.

<configuration>
   <runtime>
      <gcServer enabled="true"/>
   </runtime>
</configuration>

我假设你使用的是多核系统,否则这将是零影响.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐