0

A couple of weeks ago I was having trouble with memory leaks associated with a ContextMenuStrip. That problem was fixed. See that question here

Now I'm having similar issues with ToolStrip controls. As in the previous problem, I'm creating a large number of UserControls and adding them to a FlowLayoutPanel. Each control creates a ToolStrip for itself in its constructor. When a control is removed from the FlowLayoutPanel (the only reference to the control), it seems memory for the ToolStrip is not being released.

However when I comment out the code that creates the ToolStrip the memory leak doesn't happen.

Is this the same sort of issue as the previous one - I need to set the ToolStrip to null? I don't see how that could be since this time the control is creating the strip itself, and all the button events etc are handled inside it. So shouldn't everything be GC'd when the control is no longer referenced?

EDIT: As to the comments, the thing I don't understand is originally I was "making" my own toolstrip out of a panel and some labels. The labels were used as buttons. No memory leaks occurred this way.

The only thing I've changed is using a proper ToolStrip with proper buttons in place of the panel, but all the event handlers are wired the same way. So why is it now leaking memory?

EDIT2: I was just about to post my code but reread the question Dave linked to. It turns out it was the UserPreferenceChangedEvent problem of the ToolStrip. If I set the ToolStrip.Visible property to false, the memory leak doesn't happen!

Now, could I do this in the Dispose method? If so how? I tried copying some code but I get a compile warning: "MyToolStrip.Dispose()' hides inherited member 'System.ComponentModel.Component.Dispose()" I just don't understand the IDisposable interface.

Community
  • 1
  • 1
Dave
  • 113
  • 1
  • 6
  • 5
    95% of the time, you're registering event handlers and not unregistering them whenever you clear your controls collection. That would be the first place where I look. – Juliet May 27 '10 at 03:48
  • not sure if this question helps, but I found it interesting nonetheless: http://stackoverflow.com/questions/620733/memory-leak-in-c – Dave May 27 '10 at 03:53

2 Answers2

2

95% of the time, you're registering event handlers and not unregistering them whenever you clear your controls collection. That would be the first place where I look

(I thought Juliet's comment deserved to be an answer)

Daniel Plaisted
  • 16,674
  • 4
  • 44
  • 56
  • I just added code to make sure all event handlers have been unsubscribed before removing the controls from the FlowLayoutPanel, and memory use is still rising. Unfortunately my trial of Memory Profiler has expired. I just don't know what is going on. I haven't the knowledge to try using IDisposable yet (freaks me out reading about it :). But I don't see why that would be necessary in this case. As I said it worked fine (no memory leak) until I switched to using a ToolStrip, even without unsubscribing from the events. – Dave May 27 '10 at 05:27
0

Officially in C# memory leaks don't exist. Memory is freed some time after no one uses it anymore.

However for some cases this it too late. Especially if your object is using scarce resource you might want the object to "destruct" earlier.

Whenever you see that a class implements System.IDisposable, then the designer of the class thought it would be wise to Dispose the object as soon as you don't need it anymore. This way resources are freed way before the garbage collector would do it.

If you don't Dispose the object, the garbage collector will do it for you ... eventually. But if you want to free your resources earlier, take care that you call Dispose as soon as you don't need the object anymore.

You are talking about user controls. U user control is a Control and Controls implement System.IDisposable. So you should call dispose. If yo don't it will take some time before the resources are freed.

The most easy way to make sure the object is Disposed as soon as not needed is the using statement:

using (var myFile = File.Create(...))
{
    myFile.Write(...)
    ...
}

myFile is properly flushed / closed / disposed / finalized, even if you have exceptions or get out of the using block for any reason: return / break, whatever.

Implementing System.IDisposable is often done using a pattern. This pattern consists of creating an extra Dispose(bool) function which is called by the function Dispose and the Destructor. The bool parameter tells if you are disposing or not.

class TextWriter : System.IDisposable
{
    private StreamWriter writer = null;

    public TextWriter(string fileName)
    {
        this.writer = StreamWriter(fileName);
    }

    ~TextWriter() // destructor
    {
        this.Dispose(false); // false: I am not disposing
    }

    public void Dispose()
    {
        this.Dispose(true); // true: I am disposing
        GC.SuppressFinalize(this);
        // tell the garbage collector that this object doesn't need to be
        // finalized (destructed) anymore
     }

     private void Dispose(bool dispose)
     {
         if (this.writer != null)
         {
             this.writer.Dispose();
             this.writer = null;
         }
     }

     ...
 }

Personally I have never needed to use the boolean disposing. Officially it says:

true to release both managed and unmanaged resources; false to release only unmanaged resources.

But I can't think why I wouldn't want to release managed resources.

If you get the warning that your Dispose() hides another Dispose(), you are probably inheriting from something that implements System.IDisposable. In that case you don't need the destructor and the Dispose(), you'll only need the Dispose(bool). Look it up in MSDN and you'll see you can override it.

Harald Coppoolse
  • 28,834
  • 7
  • 67
  • 116