Memory bloat in Dynamics GP AddIn

MacroManager in GP Addin holds onto DexUIForms

Memory bloat in Dynamics GP AddIn

MacroManager Object Keeping References to DexUIForms

When using DexUIForms in Dynamics GP Visual Studio Add-Ins, make sure to remove the form from the MacroManager during disposal. Otherwise, MacroManager will keep a reference to it, keeping the form alive in memory even after it appears to be closed.

System.OutOfMemory Exception

Type: System.OutOfMemoryException
Message: Exception of type 'System.OutOfMemoryException' was thrown.

A client machine crashed twice in succession, both times mid-afternoon, with an "OutOfMemory" exception.

Fortunately, the second time, the machine was available for a memory dump. A memory dump captures a snapshot of the application in memory at that moment. This allows us to dig into what went wrong.


Looking at the memory dump, I sorted the heap by the number and size of objects. It became clear that many of the Windows Forms the user had opened (and closed) were not being garbage collected by .NET, as would have been expected.

Here we can see three references to a control that is only on one form that has been closed three times.

InitalRefCount.jpg

As I knew IDisposable was implemented, this behaviour was not expected.


Having reviewed the code again, improved the Dispose methods, and fixed a few points where event handlers were not properly dereferenced, the forms still refused to clear from memory after being closed.

Something stood out: MacroManager was still holding references to many of these forms.


Digging into Microsoft.Dexterity.Shell.dll, the Microsoft DLL that provides macro support for GP forms. When a form inherits from DexBaseForm (like DexUIForm), on creation, it automatically adds itself to a list called knownMacroableItems.

Here we have DexUIForm that inherits from DexBaseForm;
DexUIForm.jpg

DexBaseForm adds itself to the MacroManager list as the base form is created;
DexBaseForm.jpg

Here we have that method.
MacroManagerAddMacroItem.jpg

But I couldn not find anywhere in the base library where forms are removed from this list, this is the clue I needed.

The solution

The solution was to explicitly remove the form from the MacroManager list during disposal:

DynamicsGPMacroManager.jpg

MacroManager.RemoveMacroableItem(this);

I added this line to the Dispose method of our base form class. All application forms inherit from this base, which itself inherits from DexUIForm. So now, whenever any form is disposed, it removes itself from the MacroManager list too.

After this change, I ran the application again and tested memory usage. This time, forms were properly cleared from the memory heap after closing.

Here we have one reference before form closed;
BeforeReferenceCount.jpg

then after we can see the count to zero
AfterDisposal.jpg

Note

This is an easy trap to fall into—one that you might not notice unless you are actively profiling memory usage.

So if you are using DexUIForms, make sure you are cleaning up properly. Otherwise, your forms may quietly stick around in memory long after they are supposed to be gone.