Tuesday, December 2, 2008

Memory leak in WCF proxies when not aborting.

I wrote an application that samples the progress of a service.

The interface was:

public interface IProgressService
{
   int GetProgress();
}

I created a proxy for it on the client side:

public class ProgressServiceProxy : ClientBase<IProgressService>, IProgressService
{
    public int GetProgress()
    {
        return Channel.GetProgress();
    }
}

The sampling code was:

private void SampleProgress()
{
    ProgressServiceProxy proxy = null;

    while (!_shutdown)
    {
        if (null == proxy)
        {

            // Create a new proxy
            proxy = new ProgressServiceProxy();
        }

        try
        {

            // Try to get progress
            int progress = proxy.GetProgress();
        }
        catch (Exception)
        {

            // The service is down

            proxy = null;
        }

        // Wait some time for the next sample
        Thread.Sleep(250);
    }

    if (null != proxy)
    {
        proxy.Close();
    }
}

I left this code to run at night when the service was down. When I got back at morning I found that the client process occupies about 400MB of memory. With WinDBG ans SOS.dll I counted about 170000 instances of the proxy!!!!

Where is the GC when you need it?!

To solve this I first tried to do proxy.Close() in the catch block, but it doesn't work since the proxy is in faulted state.

The correct solution is to use proxy.Abort() this let the GC know that the proxy is no longer needed at release it.

The corrected code is:

private void SampleProgress()
{
    ProgressServiceProxy proxy = null;

    while (!_shutdown)
    {
        if (null == proxy)
        {

            // Create a new proxy
            proxy = new ProgressServiceProxy();
        }

        try
        {

            // Try to get progress
            int progress = proxy.GetProgress();
        }
        catch (Exception)
        {

            // The service is down

            proxy.Abort();

            proxy = null;
        }

        // Wait some time for the next sample
        Thread.Sleep(250);
    }

    if (null != proxy)
    {
        proxy.Close();
    }
}

After this fix the proxies instance count went as high as about 50.

Ami