Memory leak. Controls.Clear() doesn't free the memory.

  • Thread starter Thread starter q23r
  • Start date Start date
Q

q23r

I saw many memory leaks problems in this group.
But It seems that it's the new one.

When you add controls to the form, then delete them using
this.Controls.Clear(), memory is not released.

Simple example:

private void button1_Click(object sender, EventArgs e)
{

for (int k = 0; k < 50; k++)
{
this.SuspendLayout();
for (int i = 0; i < 100; i++)
{

CreateButton();
CreateText();
this.Text = String.Format("k={0} i={1}", k, i);
}
this.ResumeLayout(true);
this.panel1.Controls.Clear();
}
}

We create 100 buttons and textboxes, then removes them by
this.panel1.Controls.Clear().
And repeat this for 50 times.
Actually on the 22-25 time we have an exception "Can't create windows
handle".
And memory grows during each cycle.

Is it known bug or not?

For those who interested in details:

private void CreateButton()
{
Button button = new Button();
button.Location = new System.Drawing.Point(6, _position += 25);
button.Name = "Test";
button.Size = new System.Drawing.Size(75, 23);
button.TabIndex = 0;
button.Text = "Test";
this.button1.Text = ((int)(_position / 25)).ToString();
button.UseVisualStyleBackColor = true;
this.panel1.Controls.Add(button);
}

private void CreateText()
{
TextBox text = new TextBox();

text.Location = new System.Drawing.Point(6, _position += 25);
text.Name = "Text";
text.Size = new System.Drawing.Size(75, 23);
text.Text = "Text";
this.panel1.Controls.Add(text);
}
 
Stoitcho said:
I believe this is the same problem that you encountered

Unfortunately it is not the same problem.
In the bug you mentioned problem was realted to the Dock property.
I simplified my example even more. As you can see there is no panel any
more.
May be there are another properties which default values prevents to
release the memory?
//////////////////////////////////////////////////////////////////////
int i, j;
for (i = 0; i < 150; i++)
{
for (j = 0; j < 200; j++)
{
this.Controls.Add(new Button());
}
this.Text = String.Format("i={0}", i);
this.Controls.Clear();
}
//////////////////////////////////////////////////////////////////////
On my machine i have exception when i = 48.
 
q23r,

Yes your problem is different. I didn't dig too deep, but here is what I
know.
First thanks for the sample code.
From what I see you create the controls and clean the collection in a loop.
Windows Forms controls use Win32 native controls for the UI and the native
controls are message based. In other words in order everything to works fine
there must be a running message pump (loop). When controls are created and
destroyed there are messages sent to the controls. When a message is sent
from code in the same thread as the one created the control there is no need
of running message pump; the control's wndproc is called directly. When a
message is sent across the thread boundaries though, the message is
delievered via the message queue.

In your sample you block the message pump for the duration of the test, so
no cross thread messages can be delievered to the control.

You may think that there is no another thread involved here, but there is
one.

If you don't call Dispose on the controls, their native windows are
destroyed from the control's finalizer by the GC. GC calls the finalizers
from a worker thread. Here is what probably happens, as I said I didn't dig
deeply to say for sure what goes wrong, but there is what I believe happens:
A window can be destroyed only from the thread that created it (this can be
found in the win32 docs) and because the finalizer run in the GC's worker
thread the call needs to be dispatched to the UI thread. .NET does that
using the message queue. This is cross thread message operation, so either
posted or sent there needs to be message pump running, but you've blocked
it. Because of that destroying the window and maybe the whole GC attempt to
free unamaged resource fails.

Anyways the solution is to dispose the control as soon as you remove them,
so the GC doesn't have to do that. It also works if you call
Application.DoEvents after Collection.Clear, but I think the disposing
method is better, of course the best would be not to do this in a loop.

Just to wrap it up I want to say that I wouldn't consider this as a BUG or a
memory leak.
 
First of all want to thank you. I appreciate your help.

I tried your suggestions.
1. When I call control.Dispose(), for each control before
Controls.Clear(), application live longer, but I still had the same
exception.
2. When I used Appliication.DoEvents(), everyting worked fine. Except
one thing, memory still grew, but I hope GC will remove it.

Actually I put it in the cycle just to make a good example, which
everyone can run and reproduce the bug.
In our live project there is a lot of time between calls
Control.Clear(). So I still don't understand why this bug is happens.

I consider that problem is solved.
But I still think that it is a bug. At least they should write in MSDN
something more about how actually WindowsForms work, because when I
call Controls.Clear() I expect it will clear everything.

thank you once again.
 
I'd expect Cotrnol.Clear to clear everything also, but don't forget that the
GC clears the memory not the Clear method. GC runs only when there is a need
for that.

Regarding the memory leaks I guess there might be some. Windows Forms still
have memory leaks because it uses a lot of unmanaged resources. I hope with
WPF this will be solved.

How the GC works I'd recomend the follwoing articles:
Garbage Collection: Automatic Memory Management in the Microsoft .NET
Framework - Part I
http://msdn.microsoft.com/msdnmag/issues/1100/gci/
Garbage Collection: Automatic Memory Management in the Microsoft .NET
Framework - Part II
http://msdn.microsoft.com/msdnmag/issues/1200/GCI2/

Regarding the Dispose method there is alot if info in MSDN
 
Back
Top