Memory leak in Menu Items

M

Manoj Misran

All the GURUs, Need you help pleeeeaaaaaaaaase.
I am having trouble keeping an application working for more than 3-4
hours. It is a business critical app and is suppose to work 24/7.
The app runs out of memory because of a memory leak. I did the
thorough reasearch and reached to a point where I am stuck. I have
created the following sample application extracted from my original
application and it reproduces the memory leak.
Here is the code, followed by my brief explaination about the code and
my findings:
/**------------------------------------------------------------**/
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace Monitor
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.ComponentModel.Container components = null;

private System.Windows.Forms.MenuItem menuItemTransaction;
private System.Windows.Forms.MenuItem menuItemLoadTruck;
private System.Windows.Forms.MenuItem menuItemFile;
private System.Windows.Forms.MenuItem menuItemPrinterSetup;
private System.Windows.Forms.MenuItem menuItemLogout;
private System.Windows.Forms.MenuItem menuItemUpgrade;
private System.Windows.Forms.MenuItem menuItemPhInventory;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button2;
private System.Windows.Forms.Button button3;
private System.Windows.Forms.Button button4;
private System.Windows.Forms.Button button5;
private System.Windows.Forms.Button button6;

public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

//
// TODO: Add any constructor code after InitializeComponent call
//
}

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.button2 = new System.Windows.Forms.Button();
this.button3 = new System.Windows.Forms.Button();
this.button4 = new System.Windows.Forms.Button();
this.button5 = new System.Windows.Forms.Button();
this.button6 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(48, 112);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(88, 23);
this.button1.TabIndex = 0;
this.button1.Text = "AddFile";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// button2
//
this.button2.Location = new System.Drawing.Point(168, 112);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(88, 23);
this.button2.TabIndex = 1;
this.button2.Text = "RemoveFile";
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// button3
//
this.button3.Location = new System.Drawing.Point(48, 64);
this.button3.Name = "button3";
this.button3.Size = new System.Drawing.Size(88, 23);
this.button3.TabIndex = 2;
this.button3.Text = "AddMain";
this.button3.Click += new System.EventHandler(this.button3_Click);
//
// button4
//
this.button4.Location = new System.Drawing.Point(168, 64);
this.button4.Name = "button4";
this.button4.Size = new System.Drawing.Size(88, 23);
this.button4.TabIndex = 3;
this.button4.Text = "RemoveMain";
this.button4.Click += new System.EventHandler(this.button4_Click);
//
// button5
//
this.button5.Location = new System.Drawing.Point(48, 160);
this.button5.Name = "button5";
this.button5.Size = new System.Drawing.Size(88, 23);
this.button5.TabIndex = 4;
this.button5.Text = "AddTran";
this.button5.Click += new System.EventHandler(this.button5_Click);
//
// button6
//
this.button6.Location = new System.Drawing.Point(168, 160);
this.button6.Name = "button6";
this.button6.Size = new System.Drawing.Size(88, 23);
this.button6.TabIndex = 5;
this.button6.Text = "RemoveTran";
this.button6.Click += new System.EventHandler(this.button6_Click);
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 273);
this.Controls.Add(this.button6);
this.Controls.Add(this.button5);
this.Controls.Add(this.button4);
this.Controls.Add(this.button3);
this.Controls.Add(this.button2);
this.Controls.Add(this.button1);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
private void AddMainMenu()
{
this.Menu = new System.Windows.Forms.MainMenu();
}
private void RemoveMainMenu()
{
this.Menu.MenuItems.Clear();
this.Menu.Dispose();
this.Menu = null;
}
private void AddFileMenu()
{
this.menuItemFile = new System.Windows.Forms.MenuItem();
this.menuItemPrinterSetup = new System.Windows.Forms.MenuItem();
this.menuItemUpgrade = new System.Windows.Forms.MenuItem();
this.menuItemLogout = new System.Windows.Forms.MenuItem();

this.menuItemFile.Index = 0;
this.menuItemFile.Text = "&File";
this.menuItemPrinterSetup.Index = 0;
this.menuItemPrinterSetup.Text = "&Printer Setup";
this.menuItemUpgrade.Index = 2;
this.menuItemUpgrade.Text = "Upgrade";
this.menuItemLogout.Index = 3;
this.menuItemLogout.Shortcut = System.Windows.Forms.Shortcut.CtrlL;
this.menuItemLogout.Text = "&Logout ";

this.menuItemUpgrade.Click += new
System.EventHandler(this.menuItemUpgrade_Click);
this.menuItemLogout.Click += new
System.EventHandler(this.menuItemLogout_Click);

this.menuItemFile.MenuItems.Add(this.menuItemPrinterSetup);
this.menuItemFile.MenuItems.Add(this.menuItemLogout);
this.menuItemFile.MenuItems.Add(this.menuItemUpgrade);
this.Menu.MenuItems.Add(this.menuItemFile);

}
private void RemoveFileMenu()
{
this.menuItemFile.MenuItems.Clear();

this.menuItemFile.Dispose();
this.menuItemFile = null;

this.menuItemUpgrade.Click -= new
System.EventHandler(this.menuItemUpgrade_Click);
this.menuItemLogout.Click -= new
System.EventHandler(this.menuItemLogout_Click);
this.menuItemPrinterSetup.Dispose();
this.menuItemPrinterSetup = null;
this.menuItemUpgrade.Dispose();
this.menuItemUpgrade = null;
this.menuItemLogout.Dispose();
this.menuItemLogout = null;

}
private void AddTransactionMenu()
{
this.menuItemTransaction = new System.Windows.Forms.MenuItem();
this.menuItemLoadTruck = new System.Windows.Forms.MenuItem();
this.menuItemPhInventory = new System.Windows.Forms.MenuItem();

this.menuItemTransaction.Index = 1;
this.menuItemTransaction.Text = "&Transactions";
this.menuItemLoadTruck.Index = 0;
this.menuItemLoadTruck.Shortcut = System.Windows.Forms.Shortcut.F6;
this.menuItemLoadTruck.Text = "&Load Truck ";
this.menuItemPhInventory.Index = 1;
this.menuItemPhInventory.Text = "&Physical Inventory";

this.menuItemLoadTruck.Click += new
System.EventHandler(this.menuItemLoadTruck_Click);
this.menuItemPhInventory.Click += new
System.EventHandler(this.menuItemPhInventory_Click);

this.menuItemTransaction.MenuItems.Add(this.menuItemLoadTruck);
this.menuItemTransaction.MenuItems.Add(this.menuItemPhInventory);
this.Menu.MenuItems.Add(this.menuItemTransaction);
}
private void RemoveTransactionMenu()
{
this.menuItemTransaction.MenuItems.Clear();

this.menuItemTransaction.Dispose();
this.menuItemTransaction = null;

this.menuItemLoadTruck.Click -= new
System.EventHandler(this.menuItemLoadTruck_Click);
this.menuItemPhInventory.Click -= new
System.EventHandler(this.menuItemPhInventory_Click);

this.menuItemLoadTruck.Dispose();
this.menuItemLoadTruck = null;
this.menuItemPhInventory.Dispose();
this.menuItemPhInventory = null;
}
private void menuItemLoadTruck_Click(object sender, System.EventArgs
e)
{
}
private void menuItemLogout_Click(object sender, System.EventArgs e)
{
}
private void menuItemUpgrade_Click(object sender, System.EventArgs
e)
{
}
private void menuItemPhInventory_Click(object sender,
System.EventArgs e)
{
}
private void button1_Click(object sender, System.EventArgs e)
{
AddFileMenu();
}
private void button2_Click(object sender, System.EventArgs e)
{
RemoveFileMenu();
}
private void button3_Click(object sender, System.EventArgs e)
{
AddMainMenu();
}
private void button4_Click(object sender, System.EventArgs e)
{
RemoveMainMenu();
}
private void button5_Click(object sender, System.EventArgs e)
{
AddTransactionMenu();
}
private void button6_Click(object sender, System.EventArgs e)
{
RemoveTransactionMenu();
}
}
}
/**-----------------------------------------**/
The above sample app. has six buttons to create and destroy all the
menu items. If I create the full menu, its structure will be like
this:

Main Menu
- File
- Printer Setup
- Logout
- Upgrade
- Transaction
- Load Truck
- Physical Inventory

Now, if you run this sample application, and click in this sequence
"AddMain" - "AddFile" - "RemoveFile" - "RemoveMain" all the menu items
will not be destroyed and you will have a reference in the memory and
it will not be collected by GC.
BUT, if you change the sequence to "AddMain" - "AddFile" -
"RemoveMain" - "RemoveFile", now it will remove everything from memory
and its clean. I don't understand a bit why this behaviour.

Now the LAST and the most important:
Earlier I added only one submenu and it gets removed completely if I
use the second click-sequence, BUT if I add two submenus, there is no
way I can get rid of all of them in any way. By doing different
permutation and combination the most optimum that I found was the
following sequence:
"AddMain" - "AddFile" - "AddTransaction" - "RemoveMain" - "RemoveFile"
- "RemoveTransaction". Still I have three menu items in the memory and
this accumulates everytime I create or destroy the menu.

I don't know what to do now. Please see if it makes any sense to you.
You can directly write to me at (e-mail address removed)

Thanks in advance

-Manoj Misran
 
B

Bob Powell [MVP]

OTTOMH make sure that you actually dispose of the menu items that you remove
from the dynamic menus.

Just doing MenuItems.Clear won't do it if there are references to them
elsewhere.

--
Bob Powell [MVP]
C#, System.Drawing

ANNOUNCING: Well Formed.
The monthy electronic magazine for
Windows Forms and GDI+ engineers
http://www.bobpowell.net/wellformed.htm

Check out the GDI+ FAQ
http://www.bobpowell.net/gdiplus_faq.htm


Manoj Misran said:
All the GURUs, Need you help pleeeeaaaaaaaaase.
I am having trouble keeping an application working for more than 3-4
hours. It is a business critical app and is suppose to work 24/7.
The app runs out of memory because of a memory leak. I did the
thorough reasearch and reached to a point where I am stuck. I have
created the following sample application extracted from my original
application and it reproduces the memory leak.
Here is the code, followed by my brief explaination about the code and
my findings:
/**------------------------------------------------------------**/
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace Monitor
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.ComponentModel.Container components = null;

private System.Windows.Forms.MenuItem menuItemTransaction;
private System.Windows.Forms.MenuItem menuItemLoadTruck;
private System.Windows.Forms.MenuItem menuItemFile;
private System.Windows.Forms.MenuItem menuItemPrinterSetup;
private System.Windows.Forms.MenuItem menuItemLogout;
private System.Windows.Forms.MenuItem menuItemUpgrade;
private System.Windows.Forms.MenuItem menuItemPhInventory;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button2;
private System.Windows.Forms.Button button3;
private System.Windows.Forms.Button button4;
private System.Windows.Forms.Button button5;
private System.Windows.Forms.Button button6;

public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

//
// TODO: Add any constructor code after InitializeComponent call
//
}

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.button2 = new System.Windows.Forms.Button();
this.button3 = new System.Windows.Forms.Button();
this.button4 = new System.Windows.Forms.Button();
this.button5 = new System.Windows.Forms.Button();
this.button6 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(48, 112);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(88, 23);
this.button1.TabIndex = 0;
this.button1.Text = "AddFile";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// button2
//
this.button2.Location = new System.Drawing.Point(168, 112);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(88, 23);
this.button2.TabIndex = 1;
this.button2.Text = "RemoveFile";
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// button3
//
this.button3.Location = new System.Drawing.Point(48, 64);
this.button3.Name = "button3";
this.button3.Size = new System.Drawing.Size(88, 23);
this.button3.TabIndex = 2;
this.button3.Text = "AddMain";
this.button3.Click += new System.EventHandler(this.button3_Click);
//
// button4
//
this.button4.Location = new System.Drawing.Point(168, 64);
this.button4.Name = "button4";
this.button4.Size = new System.Drawing.Size(88, 23);
this.button4.TabIndex = 3;
this.button4.Text = "RemoveMain";
this.button4.Click += new System.EventHandler(this.button4_Click);
//
// button5
//
this.button5.Location = new System.Drawing.Point(48, 160);
this.button5.Name = "button5";
this.button5.Size = new System.Drawing.Size(88, 23);
this.button5.TabIndex = 4;
this.button5.Text = "AddTran";
this.button5.Click += new System.EventHandler(this.button5_Click);
//
// button6
//
this.button6.Location = new System.Drawing.Point(168, 160);
this.button6.Name = "button6";
this.button6.Size = new System.Drawing.Size(88, 23);
this.button6.TabIndex = 5;
this.button6.Text = "RemoveTran";
this.button6.Click += new System.EventHandler(this.button6_Click);
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 273);
this.Controls.Add(this.button6);
this.Controls.Add(this.button5);
this.Controls.Add(this.button4);
this.Controls.Add(this.button3);
this.Controls.Add(this.button2);
this.Controls.Add(this.button1);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
private void AddMainMenu()
{
this.Menu = new System.Windows.Forms.MainMenu();
}
private void RemoveMainMenu()
{
this.Menu.MenuItems.Clear();
this.Menu.Dispose();
this.Menu = null;
}
private void AddFileMenu()
{
this.menuItemFile = new System.Windows.Forms.MenuItem();
this.menuItemPrinterSetup = new System.Windows.Forms.MenuItem();
this.menuItemUpgrade = new System.Windows.Forms.MenuItem();
this.menuItemLogout = new System.Windows.Forms.MenuItem();

this.menuItemFile.Index = 0;
this.menuItemFile.Text = "&File";
this.menuItemPrinterSetup.Index = 0;
this.menuItemPrinterSetup.Text = "&Printer Setup";
this.menuItemUpgrade.Index = 2;
this.menuItemUpgrade.Text = "Upgrade";
this.menuItemLogout.Index = 3;
this.menuItemLogout.Shortcut = System.Windows.Forms.Shortcut.CtrlL;
this.menuItemLogout.Text = "&Logout ";

this.menuItemUpgrade.Click += new
System.EventHandler(this.menuItemUpgrade_Click);
this.menuItemLogout.Click += new
System.EventHandler(this.menuItemLogout_Click);

this.menuItemFile.MenuItems.Add(this.menuItemPrinterSetup);
this.menuItemFile.MenuItems.Add(this.menuItemLogout);
this.menuItemFile.MenuItems.Add(this.menuItemUpgrade);
this.Menu.MenuItems.Add(this.menuItemFile);

}
private void RemoveFileMenu()
{
this.menuItemFile.MenuItems.Clear();

this.menuItemFile.Dispose();
this.menuItemFile = null;

this.menuItemUpgrade.Click -= new
System.EventHandler(this.menuItemUpgrade_Click);
this.menuItemLogout.Click -= new
System.EventHandler(this.menuItemLogout_Click);
this.menuItemPrinterSetup.Dispose();
this.menuItemPrinterSetup = null;
this.menuItemUpgrade.Dispose();
this.menuItemUpgrade = null;
this.menuItemLogout.Dispose();
this.menuItemLogout = null;

}
private void AddTransactionMenu()
{
this.menuItemTransaction = new System.Windows.Forms.MenuItem();
this.menuItemLoadTruck = new System.Windows.Forms.MenuItem();
this.menuItemPhInventory = new System.Windows.Forms.MenuItem();

this.menuItemTransaction.Index = 1;
this.menuItemTransaction.Text = "&Transactions";
this.menuItemLoadTruck.Index = 0;
this.menuItemLoadTruck.Shortcut = System.Windows.Forms.Shortcut.F6;
this.menuItemLoadTruck.Text = "&Load Truck ";
this.menuItemPhInventory.Index = 1;
this.menuItemPhInventory.Text = "&Physical Inventory";

this.menuItemLoadTruck.Click += new
System.EventHandler(this.menuItemLoadTruck_Click);
this.menuItemPhInventory.Click += new
System.EventHandler(this.menuItemPhInventory_Click);

this.menuItemTransaction.MenuItems.Add(this.menuItemLoadTruck);
this.menuItemTransaction.MenuItems.Add(this.menuItemPhInventory);
this.Menu.MenuItems.Add(this.menuItemTransaction);
}
private void RemoveTransactionMenu()
{
this.menuItemTransaction.MenuItems.Clear();

this.menuItemTransaction.Dispose();
this.menuItemTransaction = null;

this.menuItemLoadTruck.Click -= new
System.EventHandler(this.menuItemLoadTruck_Click);
this.menuItemPhInventory.Click -= new
System.EventHandler(this.menuItemPhInventory_Click);

this.menuItemLoadTruck.Dispose();
this.menuItemLoadTruck = null;
this.menuItemPhInventory.Dispose();
this.menuItemPhInventory = null;
}
private void menuItemLoadTruck_Click(object sender, System.EventArgs
e)
{
}
private void menuItemLogout_Click(object sender, System.EventArgs e)
{
}
private void menuItemUpgrade_Click(object sender, System.EventArgs
e)
{
}
private void menuItemPhInventory_Click(object sender,
System.EventArgs e)
{
}
private void button1_Click(object sender, System.EventArgs e)
{
AddFileMenu();
}
private void button2_Click(object sender, System.EventArgs e)
{
RemoveFileMenu();
}
private void button3_Click(object sender, System.EventArgs e)
{
AddMainMenu();
}
private void button4_Click(object sender, System.EventArgs e)
{
RemoveMainMenu();
}
private void button5_Click(object sender, System.EventArgs e)
{
AddTransactionMenu();
}
private void button6_Click(object sender, System.EventArgs e)
{
RemoveTransactionMenu();
}
}
}
/**-----------------------------------------**/
The above sample app. has six buttons to create and destroy all the
menu items. If I create the full menu, its structure will be like
this:

Main Menu
- File
- Printer Setup
- Logout
- Upgrade
- Transaction
- Load Truck
- Physical Inventory

Now, if you run this sample application, and click in this sequence
"AddMain" - "AddFile" - "RemoveFile" - "RemoveMain" all the menu items
will not be destroyed and you will have a reference in the memory and
it will not be collected by GC.
BUT, if you change the sequence to "AddMain" - "AddFile" -
"RemoveMain" - "RemoveFile", now it will remove everything from memory
and its clean. I don't understand a bit why this behaviour.

Now the LAST and the most important:
Earlier I added only one submenu and it gets removed completely if I
use the second click-sequence, BUT if I add two submenus, there is no
way I can get rid of all of them in any way. By doing different
permutation and combination the most optimum that I found was the
following sequence:
"AddMain" - "AddFile" - "AddTransaction" - "RemoveMain" - "RemoveFile"
- "RemoveTransaction". Still I have three menu items in the memory and
this accumulates everytime I create or destroy the menu.

I don't know what to do now. Please see if it makes any sense to you.
You can directly write to me at (e-mail address removed)

Thanks in advance

-Manoj Misran
 
M

Manoj Misran

Thanks Bob,
I am disposing all the menu items individually and also clearing them
using Menu.MenuItems.Clear().
Do you mean to say that I need to remove them from the Main Menu
collection also individually instead of using Clear() function?
As far as disposing is concerned, if you see RemoveFileMenu or any other
removal function, I am disposing each and every menu items.

Thanks again. I will still take your suggestion and work on that and see
if it helps.

Manj Misran
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top