Memory leaks
As a follow up to yesterday's post here is a list of problems I found...
01: The following code for some reason causes the form holding the DirtyObjectCatcher to remain referenced.
02: This one was a real pain! DirtyObjectCatcher creates its own UndoBlock and then subscribes to it, so that whenever the user modifies an object it goes into the UndoBlock and the catcher is notified. At this point other components (such as ObjectValidator) can get a list of modified objects and do something (such as evaluate their OCL constraints).
Unfortunately this event fires at some point between the user making the change and the ECO cache being ready for the new value to be read. For this reason in the past I had to set
Application.Idle += CheckForModifiedObjects;
CheckForModifiedObjects would then trigger the relevant events in Application's Idle event so that we know the changes to the ECO cache are ready to be read. Unfortunately I made a silly mistake, I forgot to do this as the first line of CheckForModifiedObjects
Application.Idle -= CheckForModifiedObjects;
As a result the static Application class had a reference to my DirtyObjectCatcher via an event. DirtyObjectCatcher holds a reference to the form (Owner) and the EcoSpace (ServiceProvider). So the form and the EcoSpace would remain in memory.
03: In the past you could not subscribe to UndoBlock so I had to subscribe to DirtyListService. Unfortunately this would not get triggered if you activated a DirtyObjectCatcher on an object that was already dirty, or transient. To overcome this I added a CheckForUpdates method which would get every instance of DirtyObjectCatcher to check its UndoBlock for changes.
The idea was that you plug into the ECO cache and call the static DirtyObjectCatcher.CheckForUpdates whenever something changed. A pain in the ass. I requested UndoBlock.Subscribe and it was implemented within days (thanks Jonas!) but I left that code in just in case anyone was using it. Unfortunately this meant I had a static List sitting around holding references to my DirtyObjectCatchers. Again this referenced the EcoSpace and the Form, so the Form was never disposed in order to dispose of my DirtyObjectCatcher.
This wasn't noticed for some time because I use the AutoFormService to handle the lifetime of my forms, but my current app has quite a few forms where I manage the lifetime manually so the form wasn't automatically being disposed as I had expected. In addition the Form class does not call Dispose on any of its components when you call its Dispose method, which I find very strange!
Anyway, that's the lot. What a hellish week! I have uploaded the lastest version of EcoExtensions here:
www.peterlesliemorris.com/blogfiles/ecoextensions.zip
01: The following code for some reason causes the form holding the DirtyObjectCatcher to remain referenced.
if (Owner.MdiParent != null)
{
Owner.MdiParent.Activated += new EventHandler(MdiParent_Activated);
Owner.MdiParent.MdiChildActivate += new EventHandler(MdiParent_MdiChildActivate);
}
The odd thing about this is that it really is the events that matter! I subscribe Shown, Disposed, Activated on the Owner (which is a form) and that doesn't cause the same behaviour. The reason is that the Owner normally gets disposed and takes out the DirtyObjectCatcher with it, however, if I have Owner.MdiParent events referencing DirtyObjectCatcher then the form will never get disposed because DirtyObjectCatcher has a strong reference to Owner. Maybe I should change it, but for now the Shown and Activated events seem to be doing the trick.02: This one was a real pain! DirtyObjectCatcher creates its own UndoBlock and then subscribes to it, so that whenever the user modifies an object it goes into the UndoBlock and the catcher is notified. At this point other components (such as ObjectValidator) can get a list of modified objects and do something (such as evaluate their OCL constraints).
Unfortunately this event fires at some point between the user making the change and the ECO cache being ready for the new value to be read. For this reason in the past I had to set
Application.Idle += CheckForModifiedObjects;
CheckForModifiedObjects would then trigger the relevant events in Application's Idle event so that we know the changes to the ECO cache are ready to be read. Unfortunately I made a silly mistake, I forgot to do this as the first line of CheckForModifiedObjects
Application.Idle -= CheckForModifiedObjects;
As a result the static Application class had a reference to my DirtyObjectCatcher via an event. DirtyObjectCatcher holds a reference to the form (Owner) and the EcoSpace (ServiceProvider). So the form and the EcoSpace would remain in memory.
03: In the past you could not subscribe to UndoBlock so I had to subscribe to DirtyListService. Unfortunately this would not get triggered if you activated a DirtyObjectCatcher on an object that was already dirty, or transient. To overcome this I added a CheckForUpdates method which would get every instance of DirtyObjectCatcher to check its UndoBlock for changes.
The idea was that you plug into the ECO cache and call the static DirtyObjectCatcher.CheckForUpdates whenever something changed. A pain in the ass. I requested UndoBlock.Subscribe and it was implemented within days (thanks Jonas!) but I left that code in just in case anyone was using it. Unfortunately this meant I had a static List
This wasn't noticed for some time because I use the AutoFormService to handle the lifetime of my forms, but my current app has quite a few forms where I manage the lifetime manually so the form wasn't automatically being disposed as I had expected. In addition the Form class does not call Dispose on any of its components when you call its Dispose method, which I find very strange!
Anyway, that's the lot. What a hellish week! I have uploaded the lastest version of EcoExtensions here:
www.peterlesliemorris.com/blogfiles/ecoextensions.zip
Comments