内存泄漏 - Ninject,WCF,WEBApi,ChannelFactory

时间:2014-10-29 16:55:53

标签: c# wcf memory-leaks asp.net-web-api ninject

下午好。

我遇到内存泄漏问题,我想我知道发生这种情况的重点,但我不知道我需要做些什么来解决它!希望你们能再次救我。

我真的试图谷歌(很多),但我发现或测试的结果都没有实际发挥作用。

我使用了大量功能,但我必须遵循客户的IT部门的一些架构规则,因此无法考虑此主题,例如不要例如,使用Ninject或不使用WCF。我能做的(也许我需要做的)是改变它们的实施方式。

那就是说,让我们去看炸弹。

在我的项目中,表示层托管在一个服务器中,WCF服务及其所有依赖项(域,存储库等)都在另一个服务器中。

问题出在WCF服务器上,此服务器会增加从演示到服务的每个请求的内存使用量,并且在服务器内存达到限制之前不会释放此内存,并且需要重新启动服务器,或重新启动IIS应用程序池。

以下使用的技术:

域模型,WCF,带Web API的MVC4,MVC和WCF中的Ninject 3.2,用于从域到dto和反向映射的AutoMapper,Repository和NHibernate。

我已经检查了Repository和NHibernate层,他们正确地处理了他们的对象。

我正在使用Ninject在演示文稿中注入WCF,如App_Start中所示:

public class NinjectConfiguration
{
    public void Configure(IKernel container)
    {
        AddBindings(container);

        var resolver = new NinjectDependencyResolver(container);
        GlobalConfiguration.Configuration.DependencyResolver = resolver;
    }

    private void AddBindings(IKernel container)
    {
        container.Bind<IPersonService>().ToMethod(ctx => CreateChannel<IPersonService>());
        // ...
    }

    public TService CreateChannel<TService>() where TService : class
    {
        var factory = new ChannelFactory<TService>(string.Empty);

        factory.Open();
        return factory.CreateChannel();
    }
}

演示文稿Web.Config端点配置:

  <system.serviceModel>
    <client>
      <endpoint address="http://MyEndPoint/PersonService.svc" binding="wsHttpBinding" bindingConfiguration="BindClientConfig" contract="MyContract.IPersonService" />
    </client>
    <bindings>
      <wsHttpBinding>
        <binding name="BindClientConfig" maxReceivedMessageSize="2147483647" sendTimeout="00:01:00"></binding>
      </wsHttpBinding>
    </bindings>
  </system.serviceModel>

我的WEBApi配置:

public class PersonController : ApiController
{
    IPesrsonService _personService;
    public PersonController(IPesrsonService personParam) { ... }

    //Gets, Post, Put, Delete here and functional

    protected override void Dispose(bool disposing)
    {
        if (_personService != null)
        {
            _personService.Dispose(); //This point is reached
        }
        base.Dispose(disposing);
    }
}

我的WCF Dispose(合同实现了IDisposable):

public class PersonService : IPersonService
{
    private IPersonDomain _personDomain;
    public PersonService(IPersonDomain personDomainParam) { ... }

    //...
    protected virtual void Dispose(bool disposing) { ... }

    public void Dispose()
    {
        Dispose(true);  //This point is NEVER reached
        GC.SuppressFinalize(this);
    }
}

理论上,WebAPI中的_personService.Dispose必须从WCF调用Dispose,但永远不会到达此Dispose WCF。我可以从WCF到达任何其他方法,但是Dispose,我不能。

我的猜测是我在Ninject配置上遗漏了一些东西,造成了这个非管理的漏洞。

表示层似乎实际上并没有关闭与WCF的连接,这样就让端点永远在通信的另一端(WCF服务器)打开,并在那里保留分配的内存。

我已经尝试过如下更改NinjectConfiguration,但没有成功:

private void AddBindings(IKernel container)
{
    container.Bind<IPersonService>().ToMethod(ctx => CreateChannel<IPersonService>());
    container.Bind<IPersonService>().ToMethod(ctx => CreateChannel<IPersonService>()).InScope(ctx => HttpContext.Current);
    container.Bind<IPersonService>().ToMethod(ctx => CreateChannel<IPersonService>()).OnDeactivation(CloseServiceConnection);
    container.Bind<IPersonService>().ToMethod(ctx => CreateChannel<IPersonService>()).OnDeactivation(CloseServiceConnection<IPersonService>);
    container.Bind<IPersonService>().ToMethod(ctx => CreateChannel<IPersonService>()).InScope(ctx => HttpContext.Current).OnDeactivation(CloseServiceConnection);
    container.Bind<IPersonService>().ToMethod(ctx => CreateChannel<IPersonService>()).InScope(ctx => HttpContext.Current).OnDeactivation(CloseServiceConnection<IPersonService>);
    container.Bind<IPersonService>().ToMethod(ctx => CreateChannel<IPersonService>()).InScope(ctx => OperationContext.Current).OnDeactivation(CloseServiceConnection<IPersonService>);
}

public static void CloseServiceConnection<T>(T service)
{
    (service as IDisposable).Dispose();
}

也许NinjectConfiguration类的CreateChannel可以用其他方式创建,所以我可以调用channel.Close(),但我不知道怎么做。

但这一切只是猜测。你发布的所有内容我都会尝试。

感谢您的帮助,非常感谢您的时间。

Elek Guidolin

2 个答案:

答案 0 :(得分:1)

我建议为每个服务调用创建一个新的频道工厂可能是问题的一部分。创建一次,缓存它,然后重复使用它以根据需要创建新通道。如果由于某种原因每次都需要创建一个新的,请记住以后再处理它。

答案 1 :(得分:0)

我有同样的问题,除非您的WCF合同继承自IDisposable,否则永远不会调用Dispose()。

每次创建一个新的ChannelFactory都没问题,但它占用了不需要的CPU周期。我试图缓存一次ChannelFactory,但这导致Ninject无法在HTTP请求结束时删除该对象。

我通过采用我发现的一些代码来处理这个问题,这些代码在使用语句(使用Castle)中包装对WCF客户端的调用,并且还缓存了ChannelFactory,因为这非常耗费时间。有关更多信息,请访问:

https://adrianhesketh.wordpress.com/2015/03/17/wcf-client-proxy-creation-performance-with-ninject/

解决方案的来源是此博客:https://luisfsgoncalves.wordpress.com/2012/02/28/mixin-up-ninject-castle-dynamic-proxy-and-wcf-part-iii/

其他海报已经注意到一个好的方法是将Func传入您的MVC Web应用程序,因为这可以防止在可能实际不需要的情况下由DI框架创建IPersonService的实例。