Hi Chris,
thank you for your help. As I described above we basically detected, that our application needs a tremendous amount of memory and constantly allocates more and more of it.
Therefore we looked for a possibilty to find out, what causes this behaviour. So we created a class that counts the instances of classes. Each countable class includes a constructor and finalizer the increase and decreases the instances count. Because we were aware of performance issues, we added this code fragments in "IF #DEBUG" conditions so it won't be compiled into release versions.
We read about the indeterministic behaviour of finalizers that is caused by objects that stop the garbage collector to finalize them in their dispose methods. We found out that this is the reason why the finalizer of forms hasn't been called. To avoid this indeterminism we run GC.ReRegisterForFinalize during dispose (also for #DEBUG compilation).
After insertion of this command our object counter works fine even for shown forms as well as for all other (visual) components - except DataSets and DataGridViews! We tried to insert the ReRegister command into this objects too, but it still doesn't work.
We also discovered that the non-finalization of an object affects its parents. If you add a datagridview to a form, that form isn't finalized anymore.
From the starting point this behaviour would exactly explain the memory leak of our application. We created a framework that is able to create endless forms (like you can see in Access). An endless form contains panels (user controls) and each panel has a set of textboxes, comboboxes, checkboxes, etc. To be able to do the data bindings using the visual designer, we added an instance of our main dataset to each panel. This instance is disposed during the construction of the panel because the datasource property of the main bindingsource is set to the central dataset that contains the data. It exists just for designtime purpose.
As you can imagine the application will create a lot of dataset instances while showing an endlos form. Because the dataset is fairly large (about 5MB source code file) it's instance need a lot of memory.
Although the dispose method doesn't help to reduce the memory usage while showing the endless form (as we know now), it should be reduced after closing the form (=leaving the scope). Or at least the application shouldn't allocate more memory while showing the form twice (because of the windows memory management). But this is what happens - each time the form is shown, the application needs about 20MB more memory. And this behaviour would lead to massive memory problems in realtime usage.
In order to increase the overall performance we removed the dataset instances from the panels and added the data bindings manually. But there are still instances of datasets and datagridviews that should be finalized after disposing a form.
We could think of internal lists for management purpose, that contains references to datasets and datagridviews. They are not removed after the scope in the main application has been left so the garbage collector will never remove these objects. I cannot access them during debug because my application doesn't have any references anymore...
I hope this will help to understand the basic problem and maybe someone has further ideas
Thanks a lot!
Datasets aren't finalized after leaving the scope
marcin.walus
(See http://forums.microsoft.com/msdn/ShowPost.aspx PostID=65110)
Could you please give us an example of your idea We just know the "AddressOf" operator for event handling. To which dataset do you want to compare to after the object should have been destroyed (=after left the scope).
We think that there might be any internal structures or lists that reference to the dataset (or DataGridView as you can see the same behaviour with this control). Because of that the garbage collection doesn't recognize them as "not used anymore".
willstewca
I was assuming you could use AddressOf like a pointer to an object in C#
Im stumped at the mo, i might be able to provide some answers later when im at home with VS.net in front of me a my trusty reference guide.
MarlaF
Hi TAS,
I would strongly recommend against using finalizers for object lifetime bookkeeping. Finalization is not deterministic, nor guaranteed, so your object may be dead (no more live references to it), but not finalized. Also, finalization incurs a perf hit, since objects that implement finalizers need to be promoted a generation before their finalizers are run, and then collected. This includes all objects your finalizable objects refer to as well, and so on down the object graph.
A better way to determine if objects are still alive would be to use the debugger.
Is there a reason why you're monitoring object lifetimes like this The GC should collect your objects when they no longer have references to them.
Lee_Dale,
I think you're confusing Dispose() with finalizers. The GC does not call Dispose, rather it spawns a finalization thread that calls the finalizers of objects at some point after they are considered dead.
Also, you cannot view the address of an object because after each garbage collection, the object's has probably changed (unless the object is pinned, which is discouraged for perf reasons).
Hope that helps
-Chris [MS]
mnTeddy
it sounds that I have a similar problem. I believed that your suggestion is my solution too. But it isn't. I have a memory leak with datasets in conjunction with Compact Framework.
To verify the problem I have made a small program listed below. But I get an error after the fourth call (the german message is: "Fehler bei systemeigener Ausnahme"
Is there anything wrong with this code in combination to your suggestion Or is it possible that there is a problem with compact framework
Another question is: could you observe memory leaks with datasets in Compact Framework And if so, do you have a solution
Thanks a lot.
Markus
snorlaxnet
If you place a DataGridView onto a form, the form will also not be finalized. There seems to be a reference from the DataGridView to its parent form, which prevents the form of beeing finalized.
Because we use a lot of DataGridViews on our forms this should be a second reason for our memory leak.
dpuccio
Hi TAS
Thanks for the thorough description of your situation.
In the DataSet constructor there is a call to GC.SuppressFinalize(). So in your DataSet subclass, you must add a call to GC.ReRegisterForFinalize(this) in your constructor to override their SuppressFinalize call.
(Why DataSet does this, I don't know, but you can ask in the Data Access and Storage forum: http://forums.microsoft.com/msdn/ShowForum.aspx ForumID=45.)
After removing your DataSet subclass from the panels, you can add calls to GC.Collect() and GC.WaitForPendingFinalizers() (in DEBUG), which will run the finalizers for all dead objects before continuing.
If all that doesn't work, there must be a live reference (or GCHandle) keeping your DataSets alive. Unfortunately there is no way with managed code to determine what objects are keeping others alive, so you would have to use the SOS debugger extension.
Hope that helps
-Chris [MS]
billhu
Public Class CountedDataSet
Inherits DataSet
Public Sub New()
MyBase.New()
Class.SharedCounter += 1
End Sub
Protected Overrides Sub Finalize()
Form_DatasetTest.Counter -= 1
MyBase.Finalize()
End Sub
End Class
Therefore there shouldn't by any objects or references that block the garbage collection of the dataset. AFAIK a finalizer is always called by the garbage collection if there exists one.
See http://msdn.microsoft.com/library/default.asp url=/library/en-us/vbcn7/html/vaconfreeingreferencestoobjects.asp
All other components - like forms, buttons, textboxes, panels, bindingsources, etc. - are finalized very well and counted back.