0

Good Afternoon.

I'm facing a problem with memory leak, and I think I know the point where this occurs, but don't know what I need to do to fix it ! Hope you guys can save me again.

I truly have tried to google it (a lot), but none of the results I found or tests I made actually worked.

I'm using a big mix of features, but I had to follow some architecture rules from my client's IT department, so this topic can't be considered, like don't use Ninject or don't use WCF for example. What I can do (and probably I will need to) is change the way they are implemented.

That said, let's go to the bomb.

On my project, the presentation layer is hosted in one server, the WCF Services and all his dependencies (Domain, Repository, etc) are in another one.

The problem is in the WCF server, this server increase memory usage on every request from the presentation to service, and doesn't release this memory until the server memory reach the limit, and the server need to be rebooted, or the IIS Application Pool be restarted.

Below the used technologies:

Domain model, WCF, MVC4 with Web API, Ninject 3.2 in MVC and WCF, AutoMapper for mapping from domain to dto and reverse, Repository and NHibernate.

I've already checked the Repository and the NHibernate layers, and they are correctly disposing their objects.

I'm using Ninject to inject WCF in presentation as follow in 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();
    }
}

The presentation Web.Config endpoint configuration:

  <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>

My WEBApi Dispose:

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);
    }
}

And my WCF Dispose (The contract implements 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);
    }
}

In theory, the _personService.Dispose from WebAPI must call the Dispose from WCF, but this Dispose WCF is never reached. I can reach any other methods from the WCF, but the Dispose, I can't.

My guess is that I'm missing something on the Ninject configuration, that creates this unmanaged hole.

It seems that the presentation layer do not actually close the connection to WCF, that way, leaving the endpoint open forever in the other side of the communication (WCF Server) and keeping the allocated memory there.

I've already tried to change the NinjectConfiguration as below, but without success:

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();
}

Maybe the CreateChannel from NinjectConfiguration class can be created in another way so I can call channel.Close(), but I don't know how to do it.

But all this is just a guess. Everything you post I'll try.

I appreciate any help, and thank you so much for your time.

Elek Guidolin

Elek Guidolin
  • 487
  • 1
  • 8
  • 21
  • 1
    I would suggest that creating a new factory for each service call may be part of the problem. – sga101 Oct 29 '14 at 17:12
  • The real mess is because I don't know how I can call the WCF dispose in this scenario. I don't know if it's a configuration from Ninject, or the way I create the factory, and I don't know how I can call Close() from factory, which could make this call. In the presentation Server, memory is fine and disposing their objects. – Elek Guidolin Oct 29 '14 at 17:29
  • 1
    Did i get that right: `PersonController` you want it to call `IPersonService.Dispose()` *by WCF*? Or put in other words: One process (where `PersonController` lives in) is disposing objects of the other process (where `IPersonService` lives in)? – BatteryBackupUnit Oct 30 '14 at 07:16
  • In fact I want that when PersonController run his own Dispose, it could tell to the WCF: Hey, you can run your own dispose now. I think this is a task to Ninject, so probably I'm missing some configuration. TY for your reply ! – Elek Guidolin Oct 30 '14 at 12:32
  • Or tell the proxy object something like proxy.Close(), so the other side can release it's own memory. The main problem is memory leak, so I need a way that, in my presentation layer(PersonController) I could tell for WCF that is time to Dispose. And this never happend. When I place a breakpoint in Dispose's WCF method, the breakpoint is never reached. – Elek Guidolin Oct 30 '14 at 12:44

2 Answers2

1

I would suggest that creating a new channel factory for each service call may be part of the problem. Create it once, cache it, and then reuse it to create new channels as required. If you do need to create a new one each time for some reason, remember to dispose it afterwards.

sga101
  • 1,904
  • 13
  • 12
0

I had the same problem, unless your WCF contract inherits from IDisposable, then Dispose() is never called.

Creating a new ChannelFactory every time is OK, but it takes up CPU cycles which aren't required. I attempted to cache the ChannelFactory once, but this caused Ninject to be unable to remove the object at the end of the HTTP request.

I handled this by adopting some code I found which wraps the calls to the WCF client in a using statement (using Castle) and also caches the ChannelFactory since this is surprisingly time consuming. There's more information at:

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

Source of solution was this blog: https://luisfsgoncalves.wordpress.com/2012/02/28/mixin-up-ninject-castle-dynamic-proxy-and-wcf-part-iii/

Other posters have noted that a good approach is to pass in a Func into your MVC Web Application, since this prevents instances of an IPersonService from being created by the DI framework when they may not actually be needed.

a-h
  • 4,244
  • 2
  • 23
  • 29
  • I'm not anymore in that project, but I have some copy @ home. I'll try to apply, and let you know. Thank you very much! – Elek Guidolin Aug 19 '15 at 15:32