![]() |
|
|
|||||||
![]() |
|
|
LinkBack | Thread Tools | Search this Thread | Display Modes |
|
|||
|
StatusStrip control leaks its collection items
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 |
|
|||
|
Re: StatusStrip control leaks its collection items
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 |
|
|||
|
RE: StatusStrip control leaks its collection items
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 > |
|
|||
|
Re: StatusStrip control leaks its collection items
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 |
|
|||
|
Re: StatusStrip control leaks its collection items
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. |
|
|||
|
Re: StatusStrip control leaks its collection items
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 |
|
|||
|
Re: StatusStrip control leaks its collection items
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£¿ |
|
|||
|
Re: StatusStrip control leaks its collection items
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 |
|
|||
|
Re: StatusStrip control leaks its collection items
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/s9bwddyx.aspx. > 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 |
|
|||
|
Re: StatusStrip control leaks its collection items
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. |
![]() |
|
| Thread Tools | Search this Thread |
| Display Modes | |
|
|