VB.Net form garbage collection behavior
I'm creating a windows forms program and have recently noticed a large amount of memory slowly being eaten by the application. I went through my program and tried to tie up as many loose ends as I could in relation to object creation but noticed that the program would still keep memory for every form loaded until it eventually crashed.
I decided to make a simple test program which sole purpose is to pop-up a new form with a few controls, wait for 200 milliseconds, then close the form and repeat the process 30 times every time I hit a button. I found it caused the same behavior, even with the only new object creation being that of the form. So, I decided to manually invoke the garbage collector in a subroutine which handles the formclosed event. During tests I would get some memory back occasionally, but not enough to make a difference in the long run. So I start setting the form variable to nothing before I called GC.collect(). The program will initially eat a few 1000K of memory before leveling off and using only 20-40K for every 30 forms created.
Now what I'm wondering is, what is the point of an automated garbage collection system if it requires constant manual evocation and explicit dereferencing of all new objects to be effective? Have I completely lost grasp on the scope of VB.Net forms and it simply never marks forms as available memory to be cleared? I would have thought object memory references created within a form would be marked as usable when the form's close or dispose routine is called and no references to objects created within the form exist anywhere outside the closed form.
Re: VB.Net form garbage collection behavior
Garbage Collection is expensive and isn't done immediately. If you have no references to the form, then there's no need to go around manually invoking Garbage Collection. Put bluntly: the Garbage Collection mechanism is a lot smarter than you are.
Now all that remains is for your code to play fair and not hold references to things that aren't necessary to hold on to. VB has a Forms collection doesn't it? I wonder if this is holding references to forms that you create? Are you calling Close() on the forms or simply Hide()? I would expect if you called Close() then VB should play nicely and release any hidden references to the form as well.
Can you post the code of the program you used to test this?
Re: VB.Net form garbage collection behavior
Stack/Heap allocation takes time. So when you create an instance of a form, space is allocated for it... when you're done with the form, close it, dispose it and set it to nothing. The memory is still allocated for your application. If then invoke a new instance of the form, it *should* re-allocate the same space for it, re using the same memory space. But that only happens if it's the same form and the previous one was properly cleared/removed/disposed/whatever. And the new object can fit in the same footprint. That's the theory. I've seen it happen that way, as well as not...
Based on a strict reading of your post, I'd say the step you missed was to dispose your forms ... which is NOT the same as setting it to nothing. Would you mind sharing the code you used to check this out?
-tg
Re: VB.Net form garbage collection behavior
You stated that the program would crash. Is that the endpoint you reached during your testing, or just in the actual program you were working on? Seeing the memory appear to expand is not all that surprising on its own, after all, the OS could manage things that way. However, if the result was a crash, then we can pretty well rule out this being a mirage caused by OS memory management, and something is holding onto those form references such that the forms never get tagged for cleanup.
Re: VB.Net form garbage collection behavior
I've ran a few more tests without manually trying to help the GC along and it seems that the amount of memory used before tapering off increases but it eventually does stop growing. Total memory usage is around 2-3000K more than if I were to manually evoke GC.collect().
I'm sure I still have improper scopes within my initial program, though I still get memory increases on forms which do not access outside objects. Which reminds me I forgot to test whether or not the way I'm showing those forms is what's causing the scope problems.
Does anyone know if it's a bad idea to load forms into panels? Closing is done by casting the panel's only control as a form and actually using the close command. The compiler doesn't take issue with this and I know in certain cases memory is being cleared for objects within the sub-forms such as DataGridViews. For all I know the fact that the form was part of a panel's control collection could be causing my issues.
Re: VB.Net form garbage collection behavior
If you're not removing the form from the panel collection then that would [edit: probably, depending on how the Panel was being got rid of] stop garbage collection from collecting the form. Can we see the code that is exhibiting the behaviour? Without that, it's just guessing.
Re: VB.Net form garbage collection behavior
I just ran another simple test, and yeah, this is starting to look more like I just need to go through the initial program with a fine tooth comb again to figure out where I've managed to keep my forms from being marked for garbage collection.
The simple example I was talking about as far as closing the form using casting:
subForm = New Form2
subForm.TopLevel = False
Panel1.Controls.Add(subForm)
subForm.Show()
Threading.Thread.Sleep(100)
CType(Panel1.Controls.Item(0), Form).Close()
500 runthroughs later and garbage collection is indeed doing its job so it's not a control collection issue.
If anyone knows of any obscure ways of screwing up the scope of VB forms for me to look for I'd love to hear it.
Re: VB.Net form garbage collection behavior
From your last couple posts, it sounds like you are just seeing large memory usage without getting any actual crashes. This could easily be caused by a trade-off being made by the program. While allocating new memory is expensive, collecting the garbage is even more so. Therefore, it is entirely reasonable for a program to ask the OS for more memory rather than taking out the garbage. If the OS has plenty, then it might just give the app more and more, so the OS would be stockpiling memory filled with junk. Only when the GC runs will it recover any of that memory, but that doesn't mean that it will release the memory back to the OS. That would be inefficient from the perspective of the app. After all, it DID use that memory at one point, so it MIGHT need to again.
By the way, closing a form that was shown nonmodally (with Show rather than ShowDialog) behaves considerably differently than closing a form that was shown modally. I believe that closing a modal form will call dispose on it, but closing a nonmodal will not.
Aside from that, your code is holding all those forms forever. Every time through your code, you create a new form, add it to the collection (the collection holds that reference), then show form 0 (control 0, but it is a form). That is always the same form you are showing, though every time through the loop you are creating a new form and adding it to the collection...you just are never showing that new form you created, always the first one you created. Had you been showing it modally, you might have disposed of that first form when you closed it, which would mean that control(0).Show might have crashed with a Null reference exception, but since you were showing nonmodally, showing the same form again is no problem.
Re: VB.Net form garbage collection behavior
Quote:
Originally Posted by
Shaggy Hiker
I believe that closing a modal form will call dispose on it, but closing a nonmodal will not.
It's the opposite, which is why you should use a Using block to create and display modal dialogues.
Re: VB.Net form garbage collection behavior
Quote:
Originally Posted by
jmcilhinney
It's the opposite, which is why you should use a Using block to create and display modal dialogues.
Am I doing it the right way?... I don't call close or dispose.
Code:
'Main Form
Using FrmInput As New FrmInputData
If FrmInput.ShowDialog() = Windows.Forms.DialogResult.OK Then
ChangesMade = True
End If
End Using
Code:
'Form FrmInputData
Private Sub ButtonOK_Click(...
' do stuff
Me.DialogResult = Windows.Forms.DialogResult.OK
End Sub
Re: VB.Net form garbage collection behavior
Quote:
Originally Posted by
Edgemeal
Am I doing it the right way?... I don't call close or dispose.
That's what the Using block is for. The object created on the Using statement is disposed on the End using statement.
Re: VB.Net form garbage collection behavior
Quote:
Originally Posted by
jmcilhinney
That's what the Using block is for. The object created on the Using statement is disposed on the End using statement.
Thanks jm, and sorry for stepping in on the OPs thread!
Re: VB.Net form garbage collection behavior
I found this tool great for tracking down leaks (there are others, I just have experience with this and it has a free 14 day trial!). Just start it up, point it at your .exe and run it for a couple of mins and take a snapshot. Do all those actions you think should be cleared up, so opening your forms multiple times, and then take another snapshot. You can then compare them and see the delta changes, it will show all instances still in memory AND most importantly, show you what is still referencing them.
The memory leak that got me was using My.Resources. Each time you extract something, it will make a NEW copy of it, in my case I was switching between 2 icons passing into a 3rd party control. Memprofiler gave me the proof of where the issue was to give to the 3rd party to solve. (I love the old, "it must be you" general answer)
Re: VB.Net form garbage collection behavior
Quote:
Originally Posted by
jmcilhinney
It's the opposite, which is why you should use a Using block to create and display modal dialogues.
I know:sick: After I wrote that I realized I was getting modal and nonmodal backwards, but didn't fix all my mistakes.