Search code examples
c#.netmemory-managementmemory-leaksdispose

Handling memory leaks caused by an external assembly


I need to use an external assembly that I cannot modify. Suppose that I use a class from that assembly like so:

using (ExternalWidget widget = new ExternalWidget())
{
    widget.DoSomething();
}

Each time I call this code, it leaks unmanaged memory. ExternalWidget implements IDisposable and I have wrapped it in a using statement, but ExternalWidget does not clean up its unmanaged resources.

Since I do not have access to the ExternalWidget code, I cannot fix this issue the right way. Is there any other way that I can free up the memory resources used by ExternalWidget?


Solution

  • If it is a true unmanaged memory leak and you can't change the code, then there's little that you can do. The framework can't pick up on that, nor can it clean up that code.

    The approach in this case would be isolation of that component. It means there's going to be a lot of overhead in accessing it, but there's nothing else you can do.

    You can't run the code in another application domain, because the unmanaged code has no concept of the application domain.

    That leaves the process level. I'd recommend creating a service contract in WCF that mimics the calls to the ExternalWidget along with a Shutdown method.

    Then, you would create an EXE which would expose this contract (with a session, so you can hold onto the ExternalWidget instance, unless each call is stateless) through named pipe bindings.

    As a parameter to the EXE, it would take a unique identifier (use a Guid) and use that as part of setting up the endpoint for the WCF service.

    Then, you would make the calls, and when you're done with that instance of ExternalWidget, call Shutdown; the EXE would know to stop waiting and then the process will exit, and the operating system will reclaim the memory.

    Of course, there's a huge amount of overhead here, so if you find you're making a lot of calls and don't need a new process for each set of calls, you can expand the idea into a service which counts the calls and then recycles the process (the service will still have to shell out, or it will run out of resources) when needed.

    Note that if this turns out to be a managed memory issue, then you can always spin up a new application domain, run your code there (marshaling the results back and forth as needed) and then release the application domain.