[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

microsoft.public.dotnet.framework

StatusStrip control leaks its collection items

carl.clawson

7/10/2008 4:29:00 PM

The StatusStrip control leaks when you clear its Items collection. You
can try the code below. Yes, this looks like an odd way to use a
progress bar but I distilled this snippet from a moderately large
application that was running out of user handles (10000 max) after
some hours of use because of just this problem. Try it. Just make a
form with a timer, a button, a label, and a StatusStrip. Push the
button and you will see it leak user handles in the task manager.

Workaround: Explicitly dispose each toolstrip item before clearing the
collection. Set DONT_LEAK = True to do this.

Is there a better way to solve the problem? Please tell me.

I found a few articles on MSDN about leaks with StatusStrips but none
of them appear related to this. In particular I have AllowItemReorder
= False, so this is not the problem that was reported to occur when
you have that set True.

Thanks for listening,
Carl

Public Class Form1
Private Const DONT_LEAK As Boolean = False
Private progressCount As Integer
Private progressTarget As Integer = 20000
Private Sub cmdThrashStrip_Click(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles cmdThrashStrip.Click
progressCount = 0
Timer1.Interval = 10
Timer1.Enabled = True
End Sub
Private Sub UpdateProgress()
Try
If DONT_LEAK Then
' Disposing directly from collections gives
"collection modified" exception
' from enumerator
Dim killList As New Generic.List(Of ToolStripItem)
For Each item As ToolStripItem In StatusStrip1.Items
killList.Add(item)
Next
StatusStrip1.Items.Clear()
For Each item As ToolStripItem In killList
item.Dispose()
Next
Else
StatusStrip1.Items.Clear()
End If
Dim pb As New ToolStripProgressBar
pb.Value = 100 * progressCount / progressTarget
StatusStrip1.Items.Add(pb)
If progressCount >= progressTarget Then
Timer1.Enabled = False
StatusStrip1.Items.Add("Done")
End If
Catch ex As Exception
' Ha! No handle left to even put up a message box!
Label1.Text = ex.Message
End Try
End Sub
Private Sub Timer1_Tick(ByVal sender As Object, ByVal e As
System.EventArgs) Handles Timer1.Tick
progressCount += 1
UpdateProgress()
End Sub
End Class
12 Answers

carl.clawson

7/10/2008 5:49:00 PM

0

I didn't mention the other workaround: instead of adding a new
progress bar, search the collection and re-use the existing one. This
works fine in my little example. However my bigger application still
leaks when I do this, although at a much lower rate. I'll revisit that
in light of my experience with the little snippet.

-- Carl

Family Tree Mike

7/10/2008 7:52:00 PM

0

It would seem to make sense that you would have issues if you don't dispose
something that can be disposed.

Why don't you just make a single instance visible or not visible? What I do
is allocate all the controls on the status strip in the designer and use them
in place as needed.

"carl.clawson@pkinetics.com" wrote:

> The StatusStrip control leaks when you clear its Items collection. You
> can try the code below. Yes, this looks like an odd way to use a
> progress bar but I distilled this snippet from a moderately large
> application that was running out of user handles (10000 max) after
> some hours of use because of just this problem. Try it. Just make a
> form with a timer, a button, a label, and a StatusStrip. Push the
> button and you will see it leak user handles in the task manager.
>
> Workaround: Explicitly dispose each toolstrip item before clearing the
> collection. Set DONT_LEAK = True to do this.
>
> Is there a better way to solve the problem? Please tell me.
>
> I found a few articles on MSDN about leaks with StatusStrips but none
> of them appear related to this. In particular I have AllowItemReorder
> = False, so this is not the problem that was reported to occur when
> you have that set True.
>
> Thanks for listening,
> Carl
>
> Public Class Form1
> Private Const DONT_LEAK As Boolean = False
> Private progressCount As Integer
> Private progressTarget As Integer = 20000
> Private Sub cmdThrashStrip_Click(ByVal sender As System.Object,
> ByVal e As System.EventArgs) Handles cmdThrashStrip.Click
> progressCount = 0
> Timer1.Interval = 10
> Timer1.Enabled = True
> End Sub
> Private Sub UpdateProgress()
> Try
> If DONT_LEAK Then
> ' Disposing directly from collections gives
> "collection modified" exception
> ' from enumerator
> Dim killList As New Generic.List(Of ToolStripItem)
> For Each item As ToolStripItem In StatusStrip1.Items
> killList.Add(item)
> Next
> StatusStrip1.Items.Clear()
> For Each item As ToolStripItem In killList
> item.Dispose()
> Next
> Else
> StatusStrip1.Items.Clear()
> End If
> Dim pb As New ToolStripProgressBar
> pb.Value = 100 * progressCount / progressTarget
> StatusStrip1.Items.Add(pb)
> If progressCount >= progressTarget Then
> Timer1.Enabled = False
> StatusStrip1.Items.Add("Done")
> End If
> Catch ex As Exception
> ' Ha! No handle left to even put up a message box!
> Label1.Text = ex.Message
> End Try
> End Sub
> Private Sub Timer1_Tick(ByVal sender As Object, ByVal e As
> System.EventArgs) Handles Timer1.Tick
> progressCount += 1
> UpdateProgress()
> End Sub
> End Class
>

carl.clawson

7/10/2008 8:56:00 PM

0

On Jul 10, 12:52 pm, Family Tree Mike
<FamilyTreeM...@discussions.microsoft.com> wrote:
> It would seem to make sense that you would have issues if you don't dispose
> something that can be disposed.

I don't expect to have to dispose things explicitly unless there's a
reason that I need to have it done at a specific point in the code.
That's what the garbage collector is for. In the case of the Status
Strip, removed controls are not getting garbage collected. Any other
control collection I've ever used will release its contents properly
on a Clear( ) or Remove( ), and they then become eligible for garbage
collection if no other references to them exist.

If there's something special about the StatusStrip that I'm not
getting, someone please clue me in. I don't see in its documentation
that any special handling is required for it or its Items collection.

> Why don't you just make a single instance visible or not visible?  What I do
> is allocate all the controls on the status strip in the designer and use them
> in place as needed.

Good idea. My example wasn't meant to demonstrate how one SHOULD do
it. It was to demonstrate a problem. The code was silly but it
shouldn't have leaked 10,000 handles all over the floor.

-- Carl

Jack Jackson

7/10/2008 11:54:00 PM

0

On Thu, 10 Jul 2008 13:55:31 -0700 (PDT), carl.clawson@pkinetics.com
wrote:

>On Jul 10, 12:52 pm, Family Tree Mike
><FamilyTreeM...@discussions.microsoft.com> wrote:
>> It would seem to make sense that you would have issues if you don't dispose
>> something that can be disposed.
>
>I don't expect to have to dispose things explicitly unless there's a
>reason that I need to have it done at a specific point in the code.
>That's what the garbage collector is for. In the case of the Status
>Strip, removed controls are not getting garbage collected. Any other
>control collection I've ever used will release its contents properly
>on a Clear( ) or Remove( ), and they then become eligible for garbage
>collection if no other references to them exist.
>
>If there's something special about the StatusStrip that I'm not
>getting, someone please clue me in. I don't see in its documentation
>that any special handling is required for it or its Items collection.
>
>> Why don't you just make a single instance visible or not visible?  What I do
>> is allocate all the controls on the status strip in the designer and use them
>> in place as needed.
>
>Good idea. My example wasn't meant to demonstrate how one SHOULD do
>it. It was to demonstrate a problem. The code was silly but it
>shouldn't have leaked 10,000 handles all over the floor.
>
>-- Carl

There are several interrelated things going on here.

The StatusStrip controls won't be eligible for garbage collection
until all references to them are gone.

The Windows resources held by those controls will be freed either when
you Dispose them or when the garbage collector frees them.

The garbage collector knows nothing about non-managed resources like
Windows handles. Even if all references to the controls are gone, the
garbage collector won't run until the app needs more memory and that
might not occur before your app runs out of handles.

You should always Dispose any object that uses non-managed resources
when you are finished with it.

Peter Duniho

7/11/2008 1:01:00 AM

0

On Thu, 10 Jul 2008 13:55:31 -0700, <carl.clawson@pkinetics.com> wrote:

> On Jul 10, 12:52 pm, Family Tree Mike
> <FamilyTreeM...@discussions.microsoft.com> wrote:
>> It would seem to make sense that you would have issues if you don't
>> dispose
>> something that can be disposed.
>
> I don't expect to have to dispose things explicitly unless there's a
> reason that I need to have it done at a specific point in the code.
> That's what the garbage collector is for.

This is categorically WRONG.

Jack's reply addresses this, but because it's so important, let me be very
clear: if an object implements IDisposable, it's _essential_ that you call
Dispose() on the object when you're done with it. The garbage collector
is for releasing _managed_ memory. _Disposing_ objects is the exact
opposite of "what the garbage collector is for".

Pete

Jonas Yans

7/11/2008 2:34:00 AM

0

On 7?11?, ??9?00?, "Peter Duniho" <NpOeStPe....@nnowslpianmk.com>
wrote:
> On Thu, 10 Jul 2008 13:55:31 -0700, <carl.claw...@pkinetics.com> wrote:
> > On Jul 10, 12:52 pm, Family Tree Mike
> > <FamilyTreeM...@discussions.microsoft.com> wrote:
> >> It would seem to make sense that you would have issues if you don't
> >> dispose
> >> something that can be disposed.
>
> > I don't expect to have to dispose things explicitly unless there's a
> > reason that I need to have it done at a specific point in the code.
> > That's what the garbage collector is for.
>
> This is categorically WRONG.
>
> Jack's reply addresses this, but because it's so important, let me be very
> clear: if an object implements IDisposable, it's _essential_ that you call
> Dispose() on the object when you're done with it. The garbage collector
> is for releasing _managed_ memory. _Disposing_ objects is the exact
> opposite of "what the garbage collector is for".
>
> Pete

1. Create a UserControl
2. add a ToolStrip to the UserControl
3. add a ToolStripTextBox(or a ToolStripComboBox) to the UserControl
4. Create a form and add the UserControl to the form(remeber as Class
Form2)
5.Create another form(remeber as Class Form1)
6. add a button1 to form1.
7.create button1_Click
private void button1_Click(object sender, EventArgs e)
{
using (Form2 f = new Form2())
{
f.ShowDialog();
}
}

evertime click the button to show Form2,u can see GDI objects+1(Use
Windows Task Manager)

in this case,do i need Dispose this ToolStripTextBox?

carl.clawson

7/11/2008 4:37:00 AM

0

On Jul 10, 6:00 pm, "Peter Duniho" <NpOeStPe...@nnowslpianmk.com>
wrote:
> On Thu, 10 Jul 2008 13:55:31 -0700, <carl.claw...@pkinetics.com> wrote:
> > I don't expect to have to dispose things explicitly unless there's a
> > reason that I need to have it done at a specific point in the code.
> > That's what the garbage collector is for.
>
> This is categorically WRONG.
>
> Jack's reply addresses this, but because it's so important, let me be very  
> clear: if an object implements IDisposable, it's _essential_ that you call  
> Dispose() on the object when you're done with it.  The garbage collector  
> is for releasing _managed_ memory.  _Disposing_ objects is the exact  
> opposite of "what the garbage collector is for".
>
> Pete

Sorry Pete, but I don't agree. Look at the standard Dispose/Finalize
pattern. like here: http://msdn.microsoft.com/en-us/library/s9b....
You'll see that when a disposable object is finalized, its unmanaged
resources are released. That's what the Dispose(False) in the
finalizer does. You need to dispose it only if you need to control
WHEN the unmanaged resources get released, or if you need to force
managed resources to be released before they would otherwise get
finalized. A good example is if you've opened a stream on a file and
need to re-open it. If you don't Dispose (or Close) the old stream,
you have to wait an unpredictable amount of time before the file will
open again because you've left a stream hanging around with an open
file handle that won't close until the garbage collector gets to it.

Now, it's certainly a good idea to dispose things when you're done
with them. No argument there. But I don't get why it's absolutely
necessary, and I've never had a problem until I bumped into the
StatusStrip. The finalizer will eventually take care of it as long as
all references to the object are gone. I would say that any finalizer
that fails to do so is buggy. There may be some special cases with
system-wide resources that don't get released on program exit because
I don't think finalizers always get called on exit. But that's not
what we're talking about here.

Theoretical arguments aside, I can do the very same experiment with
any other control, like buttons. Create a bunch, put them on a form,
take them off. Repeat indefinitely. You can watch the handle count
grow and then suddenly drop back repeatedly as the unused buttons get
garbage collected. So...why can I get away with not disposing buttons,
but I can't get away with not disposing ToolStripItems? I tried this
with a form full of buttons just now and the handle count never rises
much above 100 before dropping back down to a dozen or two.

-- Carl

Peter Duniho

7/11/2008 5:23:00 AM

0

On Thu, 10 Jul 2008 21:37:16 -0700, <carl.clawson@pkinetics.com> wrote:

>> [...] _Disposing_ objects is the exact  
>> opposite of "what the garbage collector is for".
>>
>> Pete
>
> Sorry Pete, but I don't agree. Look at the standard Dispose/Finalize
> pattern. like here:
> http://msdn.microsoft.com/en-us/library/s9b....
> You'll see that when a disposable object is finalized, its unmanaged
> resources are released.

That's true. But there's no guarantee that an object will _ever_ be
finalized, never mind when.

> That's what the Dispose(False) in the
> finalizer does. You need to dispose it only if you need to control
> WHEN the unmanaged resources get released,

But that's all the time. There's not ever a situation in which correct
code can leave disposal of unmanaged resources up to the indeterminate
rules offered by the garbage collector.

> [...]
> Now, it's certainly a good idea to dispose things when you're done
> with them. No argument there. But I don't get why it's absolutely
> necessary,

It's absolutely necessary because without managing the disposal of
unmanaged resources yourself you have no guarantee they will ever be
managed, nor do you have any deterministic behavior regarding the disposal
and other cleanup of unmanaged resources.

> and I've never had a problem until I bumped into the
> StatusStrip. The finalizer will eventually take care of it as long as
> all references to the object are gone.

Again, there is no guarantee that this will happen.

> I would say that any finalizer
> that fails to do so is buggy.

You would be wrong then. The finalizer does exactly what it's documented
to do, but finalizing objects isn't part of its guarantee.

> [...]
> Theoretical arguments aside, I can do the very same experiment with
> any other control, like buttons. Create a bunch, put them on a form,
> take them off. Repeat indefinitely. You can watch the handle count
> grow and then suddenly drop back repeatedly as the unused buttons get
> garbage collected. So...why can I get away with not disposing buttons,
> but I can't get away with not disposing ToolStripItems? I tried this
> with a form full of buttons just now and the handle count never rises
> much above 100 before dropping back down to a dozen or two.

I can't answer the specific question because I don't have a concrete code
sample with which to compare, nor do I really care. The fact is, since
..NET never guarantees that an object will be finalized, and since you are
required by the .NET contract to dispose objects that implement
IDisposable, the fact that you leak resources when you fail to dispose
such objects is a bug in your code, not in .NET.

Disagree all you want. The fact remains that by your own admission, if
you'd followed the .NET requirement of disposing disposable objects, you
wouldn't have a problem. That may not be incontrovertible proof, but it's
sure a heck of a strong suggestion.

Pete

Peter Duniho

7/11/2008 5:25:00 AM

0

On Thu, 10 Jul 2008 19:33:52 -0700, Jonas Yans <jonas.yans@gmail.com>
wrote:

> [...]
> private void button1_Click(object sender, EventArgs e)
> {
> using (Form2 f = new Form2())
> {
> f.ShowDialog();
> }
> }
>
> evertime click the button to show Form2,u can see GDI objects+1(Use
> Windows Task Manager)
>
> in this case,do i need Dispose this ToolStripTextBoxï¼?

No. Disposing its parent takes care of disposing it.

carl.clawson

7/11/2008 2:49:00 PM

0

>
> Disagree all you want.  The fact remains that by your own admission, if  
> you'd followed the .NET requirement of disposing disposable objects, you  
> wouldn't have a problem.  That may not be incontrovertible proof, but it's  
> sure a heck of a strong suggestion.
>
> Pete

I agree that garbage collection is non-deterministic. No argument
there. I also agree that calling Dispose when possible is a very good
practice. It's what I normally do.

But I still maintain that an object that wraps an unmanaged resource
must release that resource in its finalizer. If the finalizer never
gets called, so be it. The object can't help that.

Now back to the problem at hand, I am finding that controls removed
from the StatusStrip appear to either A) never get finalized, which
means a reference to them is probably being held somewhere because
other deleted controls ARE getting finalized, or B) they are getting
finalized but not releasing their handles. OK, per our discussion,
point "A" is not an absolute proof of a problem, but it's pretty darn
good evidence. I remain convinced that there IS a problem with these
controls!

Thanks for the explanations, Pete.
-- Carl