0

I'm developing a (legacy) Windows Forms (.Net Framework 4.8) application with a main form and multiple child forms (the latter get opened and closed regularly by users). I started noticing memory leaks in our application and was able to trace them back to child forms never being garbage collected because their ContextMenuStrips remained referenced by Static variable Microsoft.Win32.SystemEvents._handlers (see below).

In order to reproduce this behavior, simply create a new Windows Forms (.Net Framework) application with two forms as follows (I'm using Visual Studio 2022):

Firstly, a child form with its ContextMenuStrip property set to a new ContextMenuStrip with one item. I've added a string property that uses up around 250 MB of memory in order to demonstrate that the resources this form is using are not being freed.

public partial class FrmChild : Form
{
    string _memoryMonster;

    public FrmChild()
    {
        InitializeComponent();
        var cmsLabel = new ContextMenuStrip();
        var tsmiClickMe = new ToolStripMenuItem("Click me!");
        cmsLabel.Items.Add(tsmiClickMe);
        ContextMenuStrip = cmsLabel;
        _memoryMonster = new string('x', 2 << 26);
    }
}

Secondly, a parent form with a button to open the child form. I have also added an additional button to be able to call Garbage Collection for testing purposes.

public partial class FrmMain : Form
{
    public FrmMain()
    {
        InitializeComponent();
    }

    private void btnOpenChild_Click(object sender, EventArgs e)
    {
        var frmChild = new FrmChild();
        frmChild.Show();
    }

    private void btnGarbageCollect_Click(object sender, EventArgs e)
    {
        GC.Collect();
    }
}

Run the application and open "Diagnostic Tools". Now try two scenarios.

Scenario 1.: Open the child form, don't interact with it in any way and close it after a few seconds. Now press the GC button to force garbage collection. The "Process Memory" part of the diagnostics windows shows a sharp increase in memory usage once the child form was opened and a decrease by the same amount once the form was closed and GC was performed. (That's what I would assume should be expected behavior.)

Scenario 2.: Same as above, but before closing the child form, right-click it, such that the ContextMenuStrip is displayed (you don't even need to click the menu item). Close the form as in Scenario 2. Now you can observe that the "Process Memory" doesn't go down after GC. If I pause the application, make a memory snapshot and investigate what is preventing the GC from disposing of the child form, I see that there is a reference to the child form through the ContextMenuStrip which is held by Static variable Microsoft.Win32.SystemEvents._handlers:

enter image description here

Using JetBrains dotMemory gives me the same insights: enter image description here

As a result such child forms never get garbage collected which, due to the complexity of our application, can lead to Out of Memory exceptions after the application has been running for a couple of hours. (An Out of Memory exception can easily be reproduced with the test application above by simply compiling it as x86 and opening/closing the child form a few times as described in Scenario 2).

My questions are:

  • Is this expected behavior and am I missing something?
  • If not, am I using the ContextMenuStrip wrongly? (I was following these instructions: How to: Associate a ContextMenuStrip with a Control).
  • Why does the ContextMenuStrip end up being referenced by the static member Static variable Microsoft.Win32.SystemEvents._handlers and what is that?

Info: I can relatively easily get rid of the problem by setting the form's ContextMenuStrip property to null as soon as the form closes. Yet the exact same problem arises for custom Controls whose ContextMenuStrip has been set (and opened). I would need to recursively run through all controls on a form and set their ContextMenuStrip property to null as well. That is doable, but it feels like that that's not how this should work.

Any insights on the issue greatly appreciated!

Krys
  • 39
  • 9

0 Answers0