PC Review


Reply
Thread Tools Rate Thread

StatusStrip control leaks its collection items

 
 
carl.clawson@pkinetics.com
Guest
Posts: n/a
 
      10th Jul 2008
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
 
Reply With Quote
 
 
 
 
carl.clawson@pkinetics.com
Guest
Posts: n/a
 
      10th Jul 2008
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
 
Reply With Quote
 
Family Tree Mike
Guest
Posts: n/a
 
      10th Jul 2008
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.

"(E-Mail Removed)" 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
>

 
Reply With Quote
 
carl.clawson@pkinetics.com
Guest
Posts: n/a
 
      10th Jul 2008
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? *WhatI 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
 
Reply With Quote
 
Jack Jackson
Guest
Posts: n/a
 
      11th Jul 2008
On Thu, 10 Jul 2008 13:55:31 -0700 (PDT), (E-Mail Removed)
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.
 
Reply With Quote
 
Jonas Yans
Guest
Posts: n/a
 
      11th Jul 2008
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?
 
Reply With Quote
 
carl.clawson@pkinetics.com
Guest
Posts: n/a
 
      11th Jul 2008
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/s9bwddyx.aspx.
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
 
Reply With Quote
 
carl.clawson@pkinetics.com
Guest
Posts: n/a
 
      11th Jul 2008
>
> 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

 
Reply With Quote
 
carl.clawson@pkinetics.com
Guest
Posts: n/a
 
      11th Jul 2008
On Jul 11, 9:38*am, "Peter Duniho" <NpOeStPe...@nnowslpianmk.com>
wrote:
>
> It's not just "very good practice". *It's mandatory for correct code.
>


Yes. I was trying to make a point and got a bit carried away. The
essential fact that I skated right over, is that the garbage collector
doesn't care about handles or other non managed resource. It will
clean up the memory when it thinks it needs to, and the handles will
get released at that point, if and when that happens.

> However, the finalizer doesn't exist for correct code. *It exists for *
> incorrect code. *That is, it's a backup plan for dealing with code thatis *
> buggy.
>


Yes. That is a much more clear statement of what I was trying to say
when I said something like "that's what the garbage collector is for."
For the record, I hate nondeterminism and struggle against it
constantly. Anyhow, I'm an old C++ guy, so I'm used to cleaning up
after myself.

Never forget the old NASA saying: "Once you start depending on a
backup system, it's not a backup system any more." Even though I've
found the GC to be pretty darn good at, um, hiding my mistakes from
me, it is not good to depend on it.

>
> In fact, if anything this illustrates why relying on the non-deterministic *
> behavior of finalization is such a bad idea. *It pushes other bugs farther *
> out, making them harder to find and fix.
>
> Pete


Yep. This was pretty hard to find, all right!

Let's just say that the StatusStrip and its items are for some unknown
reason less well behaved than other Windows Forms controls in this
regard. I am not the only person to have had trouble with them.
Anyhow, I no longer have a problem so I don't plan to goof around with
them any more.

Thanks for making me stop a moment and think clearly.
Carl

 
Reply With Quote
 
 
 
Reply

Thread Tools
Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
StatusStrip control alignment Tim Kelley Microsoft C# .NET 2 27th Jun 2007 03:17 AM
StatusStrip resize doesn't work, if StatusStrip is filled up Zytan Microsoft Dot NET 0 26th Apr 2007 10:29 PM
Designer -> 'Collection' type property -> refresh control after adding new items 12jumper@wp.pl Microsoft C# .NET 1 10th Jan 2007 04:33 PM
Cant update StatusStrip items from working thread Benny Microsoft C# .NET 1 16th Jun 2006 01:35 AM
Need some help with vb StatusStrip Control Marc Microsoft VB .NET 5 12th Mar 2006 11:03 AM


Features
 

Advertising
 

Newsgroups
 


All times are GMT +1. The time now is 12:42 PM.