· If any plug-in listen system events which will adds into EventManager (Application level object), please make sure remove the handlers before closing document.
· Avoid implement empty destructors in C#, try to implement IDisposable interface to dispose unmanaged and managed resource in Dispose method, or use dispose pattern. if any incorrect place, please correct me.
(In book of “CLR VIA C#” chapter 20, page 477, Jeffrey Richter mentioned: “the CLR doesn't make any guarantees as to the order in which Finalize methods are called, so you should avoid writing a Finalize method that accesses other objects whose type defines a Finalize method; those other objects could have been finalized already. )
Now, please refer to the following explanation why we need to notice those.
- To make it more clear, I add two commands in Windows class , this will force GC to collect garbage:
- Next, run the application, create a default document , then close the document. Run the .Net Memory Profiler tool, and attach it to the application process, then execute “Collect heap Snapshot” command. Now, Profiler will analyze the memory of the application. This process is very slow, in the end, we can see the following snapshot:
- Filter the Namespace to select the namespace - “xxx.xxx.xxx.xxx.Documents.Objects”, we can see there is still a copied IllustrationDocument object. However, we expect it should be 0. This tells us the IllustrationDocument object don’t be disposed successfully.
- Double click the IllustrationDocument row, we can get this instance’s details information that also includes its reference
- Now we found there are 19 references. But some references have no problems like Asset, because the asset object’s lifecycle is in the scope of document’s lifecycle. Check the “Only show instance included in root paths” checkbox, it will filter the references, and only show the instances included in root paths. Those references are the really killers who caused to the IllustrationDocument object couldn’t be disposed successfully:
(The row which mark up by red rectangle is caused by the Renderer class, which forgot to remove system events handler in its dispose method)
- Double click the highlight row in the above figure, we can look over which objects have references to ShotEditor instance:
- Go on (double click the highlight row in above figure), then refer to the following:
- Go on…, finally we get the following figure:
Reason: The CameraEvents has been added into EventManger class whose lifecycle is global. Any class who listens camera’s events, will cause it still keep to live, so the CLR could not mark them as garbage. This is the real reason for that we could not dispose document object successfully.
Let’s have a look at CtxMenuForm source code, we will find it listens the OnCameraEventChange event in its construction, but forget to remove this event handler.
So let’s do some changes for it, implement the IDisposable interface:
Then call form’s dispose method in someplace before close the document :
Collect the Application memory snapshot again following above steps, we will found the root references count reduce from 4 to 3, has no referenced by ShotEditor.
The some problems also exist in Renderer class and some UI class, after I do some changes for those class, remove all listened events handlers before close document, I finally remove all the root references to IllustrationDocument object, but there is still a copied IllustrationDocument object. I don’t know what’s wrong, then I refer it to MSDN, and got the following infomation:
Implementing Finalize methods or destructors can have a negative impact on performance and you should avoid using them unnecessarily. Reclaiming the memory used by objects with Finalize methods requires at least two garbage collections.
You can get more information from here.
So, I modify the Windows class, try to call GC.Collect method more than twice, run the Application application, close the default document, collect its memory snapshot, the result is there is still a copied IllustrationDocument object. I am confused! Then I turn to write a small test application, it works as MSDN said. So what’s wrong?
After some struggle, I build a release version, then I found the document can complete dispose successfully. Refer to the following figure:
Hope it helps.