Memory recovery - can anyone figure out....

cincyreds

Newcomer
Joined
Dec 3, 2004
Messages
15
Location
New York
I've been scratching my head for days trying to figure this out, but can anyone explain why even a simple application seems to hold on to resources after (supposedly) releasing them?

To duplicate specifically, create a new windows application project and add two forms. On Form1 put a button and in the button's click event put the following code:

Dim f As New Form2

f.ShowDialog()

f = Nothing

Step 1. Start the app.

Step 2. Check memory usage at this point in Task Manager. In my case, it shows about 15,920.

Step 3. Now click Button1 which will pop open Form2.

Step 4. Check memory usage again. In my case, it shows about 16,416 - a slight increase which is expected.

Step 5. Now close Form2.

Step 6. Check memory usage.

One would ***expect*** that now that Form2 has been closed and set to nothing, memory usage would return to the level it was before Form2 opened.

However, memory usage has not gone down at all - in fact, it has increased slightly! Why? Why? Why? And if you continue to open and close Form2, memory usage climbs each time.

Makes no sense.

To further confuse things further, do this:

After step 2 (before clicking the button), MINIMIZE Form1.
Wow - memory usage drops to 728k!

Now restore the window.

You would ***expect*** that memory usage would go back up to the level it was before the window was minimized.
However, memory usage now has dropped to around 2,496!

This again makes no sense. Why would Simply minimizing and restoring a form vastly decrease memory usage?

Now more importantly, is there a way to reclaim memory when completely closing a form?

Any experts out there with a solid answer?

Thanks,

:)

Dean
 
It's most likely because the "Garbage Man" hasn't come around yet. Each time you instantiate a form (when you click the button) it is actually a new instance of that form's class. Previous instances of the form are still living in memory somewhere and won't be freed up until the Garbage Collector comes around. There's no sure way to predict when this happens, but you can force it to happen if you want. Check out this page for some more info on the GC class.

Also, if you want to see when the Garbage Collector clears up your instances just define a constructor for the form's class and put a debug statement in it. When the GC cleans up the memory for that particular instance it will fire the destructor. Use a static integer variable in the class to give each instance a unique ID and then display that in the debug statement.
 
Mister E said:
It's most likely because the "Garbage Man" hasn't come around yet.
That was my first guess, but I really don't think that's it.
I'm familiar with the GC and have tried forcing it (which normally isn't a good idea). No difference.

I've let the screens stay up for around a half hour and memory consumption stays the same.
I hate to say this, but I don't think this has much, if anything to do with the GC.

As well, when the app first starts and nothing has been done, there would likely be no GC activity for the app at that point (especially a simply app like the one above that just opens up and sits there doing nothing).

So you would expect that the initial memory allocated is necessary.
But then simply minimizing the form drops memory consumption drastically and instantly.
Why would the app, in 2 IDENTICAL states need 16 megs of ram in one instance and only 2 in the other?

I've always thought that once a form is disposed and set to nothing, it is gone and all resources released.

Obviously this is not the case.

I've tried all kinds of tricks to reduce memory consumption consistently. Right now I'm using one that is really goofy, but drops memory consumption.
Basically, it creates a new form, sets Opacity to 0, ShowInTaskbar to false, WindowState to minimized and location to 8000 (to position it off screen for good measure).
It then does a form.Show(), then minimizes it, then the subroutine exits.
Calling this from my app after I close a form drops memory usage instantly.

Although this works, it still doesn't make sense.

The only other thing I can think of is that GDI objects are not being released.
Not sure, and I don't know much about reclaiming GDI resources.

:)
 
Last edited:
So you would expect that the initial memory allocated is necessary.
But then simply minimizing the form drops memory consumption drastically and instantly.
Why would the app, in 2 IDENTICAL states need 16 megs of ram in one instance and only 2 in the other?
Because when its visible it needs to have memory available for the canvas it draws itself on. Since apps draw themselves they use their own memory to hold this memory. When you minimize there is no visible presence and the memory footprint seems to lower. It doesn't actually lower, the pages simply become availble for other processes should there be memory pressure. The vm size for the application doesn't change.
I think, i may be a bit off but the general idea is right.

I've always thought that once a form is disposed and set to nothing, it is gone and all resources released.

No. When you dispose it will clean up any unmanaged resources (and perhaps some larger managed ones) and should remove the object from the finalization queue. However, the object still exists and references to it will still be valid which is why the framework has the ObjectDisposedException. When you set it to null you simply set the reference to null which doesn't affect the object at all.

The object will be destroyed when a collection occurs if and only if there are no active roots for the object. If there is any single active root such as a stack based reference a GC reference/handle or one of the more obscure references then the object won't get collected even if it has been disposed.

Garbage collections really don't very often happen unless your app hits memory pressure in some way, there isn't really a reason to collect anything most of the time and it can only hurt performance if it happens while your app is working.

I've tried all kinds of tricks to reduce memory consumption consistently. ...snip...
Although this works, it still doesn't make sense.
Just how much memory are you talking about here? If the app feels it needs the memory why are you second guessing both windows and the .NET framework. Its unlikely to be a leak, not impossible but unlikely. The memory is being used for some reason or it wouldn't have been allocated. Let the underlying systems do their job and worry about making your app a good experience for the users.

When the GC cleans up the memory for that particular instance it will fire the destructor.
In spirit it is intended this is true, but it isn't entirely accurate. The design guidelines say that the .Dispose method of items implementing the IDisposable interface should remove the object from the finalization queue using GC.SuppressFinalization(), if you follow this guideline and call the dispose method (explicitly or implicitly including using the using pattern etc) the Finalize (destructor in c#) will not be called when the object is collected. If your class is disposable do your cleanup in .Dispose() and call it from the .dtor method.
 
Last edited:
Careful with that GC.Collect, it can push objects still referenced into a later garbage collection generation. These "survivors" are then about an order of magnitude less likely to be restored to the heap on the next garbage collection.

Are you noticing slowdown? Lots of (performance effecting) pf usage? Or just a big number in the task manager? Those big numbers scare me, but we are supposed to trust the garbage collector to do its job and play nice. It's not our job to manage memory (beyond disposing and freeing references); that's left up to the framework. Make sure you are freeing all references, and properly disposing your objects. The memory may not be reclaimed until the GC determines that an app (yours or any other) needs it, so pay more attention to noticable performance issues than numbers in the task manager.
 
Wraith said:
Because when its visible it needs to have memory available for the canvas it draws itself on.
Make sense. Thanks for the explanation.

Garbage collections really don't very often happen unless your app hits memory pressure in some way, there isn't really a reason to collect anything most of the time and it can only hurt performance if it happens while your app is working.
Absolutely. I only tried GC.Collect() to see what would happen, but I do realize this is bad practice and the app is not doing this.

Just how much memory are you talking about here?
The difference between 900k and up to 20 megs. Part of the reason in wanting to keep footprint small is for user perception. I plan on releasing the app publicly and part of its appeal will be the small memory consumption.
Because it's something that would be running 24/7, it's important to use as little memory and cyles as possible so I've approached this with the attitude of dropping resource requirements to as low as possible rather than having a "well, it could be lower, but it's probably good enough."
If anyone else is like me, when I install a new app which has to run for an extended time and I see it consuming 12 megs of ram and a good amount of clock time, it better have a damned good reason for being there or it gets uninstalled.
That's the rationale behind my "quest" for lowest possible resource requirements.

If the app feels it needs the memory why are you second guessing both windows and the .NET framework.
Hmm. Not really second guessing, just optimizing. Part of the reason is for appearance as remarked in my reply to the last statement. Besides actually **using** few resources, I want my app to **appear** to always be using few resources. I suspect most end users do not know what the "garbage collector" is, let alone how it works. I can't explain in the readme.txt that "if you see it using lots of resources, don't worry - the GC will eventually clean it up."
As well, I'm not using GC.collect or anything really fancy. Just trying to free resources asap.

Its unlikely to be a leak, not impossible but unlikely. The memory is being used for some reason or it wouldn't have been allocated.
That's exactly what I thought, but it now seems that initial allocation may be based on some kind of pre-existing algorithm to determine the **potential** of resource needs. Obviously something different is happening when, like I mentioned, a window in 2 identical states (which you'd think would have the same allocation requirements) uses vastly different amounts of memory. Frankly, I'm just not technically smart enough in this area to really explain it.

Let the underlying systems do their job and worry about making your app a good experience for the users.
Absolutely. Usability is king. This whole memory thing is just one facet of the app. I've spent a LOT of time tweaking every little thing to make sure end user experience is optimized, makes sense and easy to use. But I do lump resource usage into that experience on a certain level. Like I mentioned, I for one will uninstall an app if I see unacceptable resource usage.
I love those little apps written by good C programmers. Resource usage is always spartan. Although I know the overhead of the CLR can make that difficult, I'm shooting for as low as I can get.
Right now I've tweaked this as much as I can and I think I'm happy with resource usage at this point. When the app is idling (which is probably 98% of the time), it uses around 900k and runs on a low priority thread. I'm using a separate system timer that fires in the background at a regular interval which does a quick call to my memory cleanup routine. It's funny, but if the app sits in the tray doing NOTHING, resource usage will slowly continue to climb. Makes no sense because NOTHING is being done except for having the main system timer (not a windows form timer) running. Even if its interval is set for days in advance, the app does nothing but sit in the tray waiting for that timer to fire. Running the memory cleanup routine keeps things in check.
The ONLY thing the cleanup routine does is issue the following commands to the form running in the tray:

FormTray.Show()
FormTray.Windowstate=Normal
FormTray.Windowstate=Minimized
FormTray.Hide()

The form itself is set to NOT show in the taskbar and has a startup state of minmized, so when it is technically shown, you can't see it.
Just issuing the 4 statements above seems to force the app to drop resource usage.

For the hell of it I'll post a link to the finished product here if you wanna have a look-see.

Thanks for your great answers - they helped!

:)

Dean
 
so pay more attention to noticable performance issues than numbers in the task manager.
Definitely. And performance is fine. But the numbers in the task manager are important as well. If someone **thinks** my app is using too many resources (even when in fact it isn't), it may get the 'old heave-ho anyhow.
So, not only do I want to use few resources, I want it to **appear** to use few resources as well.
OK - maybe I'm being a little anal, but solving this dilemma is also a good learning experiecne.
As for GC.collect - I'm not using that at all. I only tried it to see what would happen, then removed it from code.
Right now the app is 100% managed code.

:)

Cheers,

Dean
 
And performance is fine. But the numbers in the task manager are important as well. If someone **thinks** my app is using too many resources (even when in fact it isn't), it may get the 'old heave-ho anyhow.
If a user looks at the numbers in the task manager and doesn't understand them they might think the memory usage of your app is a bit big. On the other hand they are making a number of errors in that decision and much as we may like to grab them and shake sense into them from time to time you really can't make users sensible. If they bad decisions through misinterpreting data then you have to let them, overall it is their loss.
I understand your reasons but i think perhaps you are over weighting them.

The ruintime has a set of tuned parameters it uses, when a process starts it'll make a number of allocations for stacks heaps etc. These numbers are abritrary and are tuned for an average process. You may need more stack or heap space or you may need less. Until you get to actually executing the code you really can't tell. If there is unused space then you could i suppose think of it as wasted. In reality these lazy little things help you by not taking processor time to maintain a minimum environment for your application. Unless there is memory pressure nothing else is going to want the wasted memory, you only waste time freeing it and resizing everything.

This ties into my comment about GC Collect frequency. I wasn't trying to tell you that GC.Collect is usually a bad idea, you already knew that. I was pointing out that no matter what you allocate and deallocate unless there is a good reason for a collection to occur like memory pressure heap fragmentation etc then it won't. The program can just keep allocating objects and discarding them without them being removed from memory.
 
Wraith, thanks for the comments. Yeah - it'd be nice to shake some users from time to time <grin>.

I agree with you, though as programmers it is our job to make an app tailored to the end user. If the end user is ignorant, we should take that into account.

I'll use the analogy of those cheesy infomercials you see all the time advertising absolutely useless junk. If they decided to design their commercials to appeal only to smart people who aren't gullible, they would never sell a single unit.
Instead, they appeal to morons and suckers and sell the crap out of their junk.

So it is with programming (though hopefully we're not writing crap!).
If I can keep my app in the hand of an ignorant user by masking the technical stuff they don't understand, then great.
A good programmer will make the program appealing to a wide audience.

Yeah - maybe I'm over-thinking this, but I always consider the end user even if the end user is a technophobe.

Anyhow, thanks for the comments and have a great weekend!
 
Just as an aside - I'm pretty sure that when an application is minimised, Windows forces a GC on the application (be it a .NET app or ANY other app) because it is assumed that the application is being put into a 'semi-hibernative' state

No idea why I think this, and I can't back it up with any concrete information, I'm just sure I read it somewhere in the MSDN docs.

This would explain why restoring/minimising the (only) form in your application reduces memory usage - its effectively triggering this global GC.

I'll see if I can't dig up some facts on it, not just hearsay.

B.
 
penfold69 said:
I'm pretty sure that when an application is minimised, Windows forces a GC on the application (be it a .NET app or ANY other app) because it is assumed that the application is being put into a 'semi-hibernative' state.
No idea why I think this, and I can't back it up with any concrete information, I'm just sure I read it somewhere in the MSDN docs.
This would explain why restoring/minimising the (only) form in your application reduces memory usage - its effectively triggering this global GC.
I think you're on to something. As well, ONLY when the last form of the app is hidden does the memory drop, so what you're saying seems to make sense.
Thanks.
 
Back
Top