Memory leaks in the .NET 2.0 Ping class

November 14, 2006

As an IT developer, the .NET 2.0 System.Net.NetworkInformation.Ping class is extremely useful. Many times I’ll need to see if a server is reachable, or whether something exists at an IP address before sending another network request (say, a WMI query) with a long timeout delay. Although the Ping class works great (and is much easier than writing your own ping networking code, as in .NET 1.1), it can easily lead to a memory leak in your application. I remember when I first started with C#, there was the feeling that “everything is garbage collected, so that means I don’t have to worry about memory management.” I soon learned that wasn’t true at all, especially with Ping.

Let me post some sample code, illustrating the correct way to send an asynchronous ping. I’ll explain it further below.


private void Refresh( Object sender, EventArgs args )
{
    Ping ping = null;

    try
    {
        ping = new Ping();
        ping.PingCompleted += PingComplete;
        ping.SendAsync( defaultHost, null );
    }
    catch ( Exception )
    {
        ( (IDisposable)ping ).Dispose();
        this.isAlive = false;
    }
}

private void PingComplete( Object sender, PingCompletedEventArgs args )
{
    this.isAlive = ( args.Error == null && args.Reply.Status == IPStatus.Success );
    ( (IDisposable)sender ).Dispose();
}

You can see the Ping class implements the IDisposable interface. IDisposable, if you’re new to .NET, simply means you must call Dispose() before your object can be garbage collected. What makes the Ping class difficult compared to other IDisposable classes is that calling Ping.Dispose() doesn’t actually do anything. It might not be apparent at first, but if you’re working on a monitoring application that sends a new ping every minute (or less), you’ll notice your application’s memory usage creeping up to the hundred of megabytes before long.

The reason behind this is due to some tricky issues with inheritance and interfaces. You see, Ping inherits from System.ComponentModel.Component, which also implements Dispose(). Instead of overriding the inherited Dispose() method to actually dispose of the object, the Ping class explicitly implements the IDisposable Dispose() method. This means you must explicitly call IDisposable.Dispose(), or you’ll end up with the inherited Dispose() method which doesn’t do anything useful at all in this case. You can do this by casting the Ping object to IDisposable, hence the ( (IDisposable)ping ).Dispose() in the example above.

If this seems like a strange design decision, you’re not alone. I’d love to know why Ping implements IDisposable in this way, so feel free to post a comment if you know the answer.

Marc Charbonneau is a mobile software engineer in Portland, OR. Want to reply to this article? Get in touch on Twitter @mbcharbonneau.