Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Form unload does not release memory! 1

Status
Not open for further replies.

sohelcse

Programmer
Jul 22, 2001
2
0
0
BD
We have an MDI application with 30 child forms. 'Orders' form is loaded from menu using this command - frmOrders.show. When user presses a 'Close' button in the form, the form closes by calling Unload Me. Keeping the Win2K Task Manager open, I watched that before the form is shown the program was using 6M of memory. After the form
is shown the used memory becomes 7.8 MB. Now when the form is unloaded, it still is showing the same memory use! But subsequent call to the same form does not use any extra memory.

I searched through the newsgroup and saw a solution which says - 'set frmOrders = Nothing' statement will release the memory back to windows. But IT IS NOT TRUE. I tried with a small program with two forms but found no truth in it.

After each form is shown, the memory use increases and the program starts slowing down. Any solutions?

I am using:
Windows 2000 Professional
Visual Basic 6 (+SP5)
 
My guess is that it's a control on the form that's being loaded and not releasing the memory. I would set each of the controls on the form to nothing in the form unload event.
 
are you opening any other forms/objects trough your orders form?
if possible, post all code related to this form.

Regards
 
Put this in your frmOrders Form

Private Sub Form_Terminate()
MsgBox "frmOrders Terminated"
End Sub

If you see the message then the form is not only unloaded but the class is terminated. If you do not see it, it means that the form would not terminate because there is still a reference to it, and all the form level variables are still taking up memory. Be sure that there arew no form level variables in the form itself that contain a reference to the form OR ANY CONTROL on the form or any MDI children. If so, they must be set = Nothing, probably best in the Form_Unload event. That is also the place to set frmOrders = Nothing. Forms/Controls Resizing/Tabbing Control
Compare Code (Text)
Generate Sort Class in VB or VBScript
 

There is another possibility, however unlikely it may be, but since there is no way of accessing the garbage collector with VB it may be that your code is perfectly fine but the extra memory has not been reclaimed by the OS, and will show that way until the OS decides it needs that memory back or for some reason the garbage collector is activated. I have seen this happen a few times and it usually does not take that long (at most a couple of seconds) but the most likely case has been put forth by JohnYingling.

Good Luck

 
None of the above. VB does not remove any form from memory, ever. It hasn't VB4 32bit. The problem is the Forms collection. It is reference to a hidden global that allows us to avoid having to say:

Code:
Set frmMain = New frmMain
  frmMain.Show

It also allows us to treat all module level form variables as Properties, because that is exactly what they are.

However, it means that no amount of garbage collection, which VB does not have, BTW, could reclaim that memory because it's referenced in the VB runtime.

Source: Francesco Balena, "Programming Visual Basic 6.0"
Chapter 9 "Advanced Forms and Dialogs"

Check this out with the following:
Code:
cmdRatOutForms_Click()
    Dim frm As Form
    
    Debug.Print String(20, "=")
    Debug.Print "Forms Collection " & Now
    Debug.Print Forms.Count
    For Each frm In Forms
        Debug.Print "    " & frm.Name
    Next frm
End Sub

Try any combination of openings and closings you like and rerun this code.

But cheer up, at least you don't have write your Windows programs with C. I used to do that, I'd change careers before I did that again.
 
Here's Tuna Doing Jonny Carson...

I did not know that.


Seriously though, jpittawa, good answer. [fish] No Dolphins were harmed in the posting of this message... Dolphin Friendly Tuna!
 
VB does not remove the form code but it does release all module level variables just before the form class terminates. Form level variables can only be accessed as "Properties" when they are declared Public.

Try this

Private Sub Form_Load()
Dim frm As Form
Debug.Print "Before 2 Shown"
For Each frm In Forms
Debug.Print frm.Name
Next
Form2.Show
Debug.Print "After 2 Shown"
For Each frm In Forms
Debug.Print frm.Name
Next
Unload Form2
Debug.Print "After 2 Unloaded"
For Each frm In Forms
Debug.Print frm.Name
Next

End Sub

Form 2 is gone after being unloaded when no other variables hold a reference to it. Forms/Controls Resizing/Tabbing Control
Compare Code (Text)
Generate Sort Class in VB or VBScript
 
Add this to Form 2.

Private Sub Form_Terminate()
Debug.Print "Form2 terminated"
End Sub

Then add this after the previous debug statements.
Debug.Print "Before Set Form2 = Nothing"
Set Form2 = Nothing

You will see that Form2 does not terminate until the global implicit "As New" variable of the same name us set to Nothing. Before this, Form 2 is "Unloaded" but Form-Level variables still hold their values until the form terminates.

"Debug Display"
Before 2 Shown
Form1
After 2 Shown
Form1
Form2
After 2 Unloaded
Form1
Before Set Form2 = Nothing
Form2 terminated Forms/Controls Resizing/Tabbing Control
Compare Code (Text)
Generate Sort Class in VB or VBScript
 
JohnYingling has delimited wright. Add those 5 lines before End sub statment in Sub Form_Load
If Not Form2 Is Nothing Then
Debug.Print "Form 2 is NOT Nothing"
Else
Debug.Print "Form 2 is Nothing"
End If

You got
After 2 Unloaded
Form1
Form 2 is NOT Nothing
Form2 terminated

Therefore Form2 is not unloaded until app is finished. This is because Form2 is not generated with new statement and has always reference at least one (if you unload it) until app is finished.
Bye
 

>Therefore Form2 is not unloaded until app is finished

Um, not quite.

If you do this:

'If Not Form2 Is Nothing Then'

you will cause the form to re-initialize!

The hidden variable is the same as coding this:

Dim frmOrders as NEW frmOrders

try this:
Put a debug.print into the frmOrders's initialize and terminate events, identifying that the form is initializing and terminating.

Put this into a proceedure.

Dim frm as NEW frmOrders

frm.show 'Form's intialize event fires
unload frm
'release form
set frm=nothing 'Form's terminate event fires

debug.print frm Is Nothing 'Oops!

Guess what? The Form's intialize event fires again!
Because you have referenced it again!

So, in the debug window you will see that the form intializes, terminates, and intializes again.
It will not terminate until the variable goes out of scope, or you set it to Nothing...and in the case of the vb hidden variable, it never goes out of scope by itself until the program terminates. So, you have to set it to nothing to reset the variables.

Same this if you declared the variable at form class level.
It will not go out of scope until the parent form unloads and terminates.

So, the hidden object variable will always be available.
But you can release additional memory occupied once loaded, by destroying it, setting it to nothing.

You probably should always declare a seperate object variable:

Private Sub OpenOrdersForm()

Dim frm As frmOrders
Set frm = New frmOrders

frm.Show

End Sub

Now, as soon as frmOrders closes, the object will automatically be set to nothing.
If you check if the object Is Nothing, it will not re-initialize again, even if the 'Dim frm As frmOrders' was declared at form level, unless you declare it 'As New'.





[/i][/u][sub]*******************************************************
General remarks:
If this post contains any suggestions for the use or distribution of code, components or files of any sort, it is still your responsibility to assure that you have the proper license and distribution rights to do so!
 
CCLINT,

Great points. I've been chasing my tail over this one.

It I just ran the following to prove it all works.
Code:
Option Explicit

Private Sub OpenOrdersForm()

    Dim frm As frmOrders
    Set frm = New frmOrders

    frm.Show

End Sub

Private Sub cmdOpen_Click()

    OpenOrdersForm
    
End Sub

Private Sub cmdRatOutForms_Click()
    Dim frm As Form
    
    Debug.Print String(20, "=")
    Debug.Print "Forms Collection " & Now
    Debug.Print Forms.Count
    For Each frm In Forms
        Debug.Print "    " & frm.Name
    Next frm
End Sub
 
I posted the initial message of this thread. After all these discussions, I am more confused because my problem is not solved. After 5/6 forms have been opened/closed, the application memory use is just increasing and slowing down...

My simple question is : HOW CAN I RECLAIM A FORM MEMORY AFTER IT IS CLOSED (IN VB6)?

Real solutions please.

 

If you are following the proper guidlines for loading and unloading a form (can be taken from above) then I believe it may be because of some other factor not considered.

We are a little left in the dark by your introduction, if there is more to this than simple opening and closing of forms, and until now, we have only given help based on just this.


If you are seeing it slow down that much with just a few forms, then I'd check what each form is loading and how it is loading other objects.

I think the rest of us do not see this problem, (at least to this extent) and you may be just not following some basic guidlines. I do not know.

Try using the VB data Form generator and then see what happens when just this form gets loaded and unloaded.

Are you using ADO objects? If so, Which ones and how are you declaring it and is it being closed in code correctly?

Are you loading any reports?

Are you using any other 3rd party objects not shipped with VB6?

Are you loading anything else other than that form (for instance, another form)?`

Try using the process of elminination, and start removing the calls to methods and properties to objects outside of the form, then start removing some objects used inside the form.

Basically, what does the form do or what is it's purpose?

[/b][/i][/u][sub]*******************************************************
General remarks:
If this post contains any suggestions for the use or distribution of code, components or files of any sort, it is still your responsibility to assure that you have the proper license and distribution rights to do so!
 
>Real solutions please.

Oddly, this is not a free technical support site.

That being said, as CCLINT has very gently pointed out, the basic information concerning when a Form actually releases its memory has been covered reasonably comprehensively in this thread.

To summarise:

1. Unload does not free the memory, it merely removes the visual components.

2. The memory gets released when the form terminates properly, which happens when the reference count to the form becomes zero.

3. You can decrement the reference count by using the "Set FormObject = Nothing" method. In a simple scenario (i.e you have only got one reference to the form before you make this call) this will cause the reference count to become zero, and the form will terminate and memory will get freed up. This may not be reported in Taskmon for sometime, however.

4. If the form is not actually terminating, then there is a high probability that this is because the reference count has not been decremented to zero, i.e. you still have a reference to that form somewhere.
 

>you still have a reference to that form somewhere.

Very well could be the problem.
Or an outside reference to an object loaded by the form (whether a control or some other object such as ADO).
This is one of the biggest mistakes made with respect to similar problems of forms not getting unloaded.

But, if after checking the forms collection and finding out that the forms are really not loaded anymore, then the problem would more than likely not be an outside reference.

I could visualize a problem like this happening which something like a data report (VB or CR).

Again, this biggest issue that I see is the fact that the program is slowing down. 4 or 5 basic forms, or even 10 or 20 on a pc with standards from the past few years, should not have a problem with this, unless there are alot of/heavy controls loaded onto the form, or other objects loaded by the form.

Put the following into a public sub, start the program, load/unload the forms in question, and then call the funtion (either from a menu point or put the program into breack mode):

Dim frm As Vb.Form
For Each frm in Vb.Forms
Debug.Print frm.Name
Next frm

Then you will see which forms, which you thought you unloaded, are really still loaded. [/b][/i][/u][sub]*******************************************************
General remarks:
If this post contains any suggestions for the use or distribution of code, components or files of any sort, it is still your responsibility to assure that you have the proper license and distribution rights to do so!
 
CCLint,

Unload does take the form out of th Forms Collection even though there are other references as I showed above. There can still be a reference in the implicit form variable that keeps the form from terminating.

I had this problem because I had references IN the form level variables to controls ON the form itself. These qualify as circular references. The form level variables would not go away until the form terminated and the form would not terminate until the reference count went to zero but the reference count would not go to zero until I explicitly set those varaibles set to Nothing.

That is one reason, maybe the main reason, that .NET Framewirk gave up on reference counting in favor of Garbage Collection. Forms/Controls Resizing/Tabbing Control
Compare Code (Text)
Generate Sort Class in VB or VBScript
 
sohelcse, I can empathise with the confusion here. Do put the routine CClint suggested in your primary form and test to see if your program has really unloaded the forms you thought it did.

Here is one way. Put a command button on that form and name it whatever you choose. With my sense of humor, I called it "cmdRatOutForms." Double clicking the command button will open the code behind the form on the Click event. I have re-entered that code before.

Code:
Private Sub cmdRatOutForms_Click()
    Dim frm As Form
    
    Debug.Print String(20, "=")
    Debug.Print "Forms Collection " & Now
    Debug.Print Forms.Count
    For Each frm In Forms
        Debug.Print "    " & frm.Name
    Next frm
End Sub

If this code shows the forms are still open, then you have a problem. CClint's example shows the best way to open a form:

Code:
Private Sub OpenOrdersForm()

    Dim frm As frmOrders
    Set frm = New frmOrders

    frm.Show

End Sub

The "frm" variable is a reference. It will be destroyed when the OpenOrdersForm stack is torn down on exiting the subroutine. The form is left open because the "frm" was only a reference to a specific instance of the form, it is not the instance itself.

Should you need to, you can disconnect the reference programmatically by saying
Code:
Set frm = Nothing
.

Now, if those forms have references to any object that still exists when you try to remove them from memory, then they cannot be removed from memory because those references are still marked as "in use", i.e. References.Count > 0.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top