This is a migrated thread and some comments may be shown as answers.

Using RadMenu in an MDI Parent & Child with button menu items leaks memory

6 Answers 194 Views
Menu
This is a migrated thread and some comments may be shown as answers.
Fred
Top achievements
Rank 1
Fred asked on 29 Nov 2010, 06:24 PM
Hello,

I've got this memory leak, could you guys give an explanation/workaround? I've made the simplest test case (see below), factors for the bug to occur (that I already discovered):

- it's an MDI form situation with parent & child
- the child is maximized (the menu items are added into one menubar)
- the menu item provided by the client is a menu button
- a textbox is on the child form

TestMdiParent.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
 
using Telerik.WinControls.UI;
 
namespace MenuMemoryLeak
{
    public partial class TestMdiParent : Telerik.WinControls.UI.RadForm
    {
        public TestMdiParent()
        {
            InitializeComponent();
 
        }
 
        //NOTE this stuff with the backgroundworker is just to repeatedly close/open the form, I know it's not fully correct but the bug occurs nevertheless
 
        BackgroundWorker myWorker = new BackgroundWorker() { WorkerReportsProgress = true, WorkerSupportsCancellation = false };
        TestMdiChild myMdiChild;
 
        private void TestMdiParent_Shown(object sender, EventArgs e)
        {
            myWorker.DoWork += delegate
            {
                int count = 0;
                while(true)
                {
                    ++count;
                    myWorker.ReportProgress(count);
                    System.Threading.Thread.Sleep(1000);
                }
            };
 
            myWorker.ProgressChanged += delegate
            {
                if(myMdiChild != null)
                {
                    myMdiChild.Close();
                    System.GC.Collect();
                }
                myMdiChild = new TestMdiChild() { MdiParent = this };
                myMdiChild.Show();
            };
 
            myWorker.RunWorkerAsync();
        }
    }
}

TestMdiParent
.Designer.cs
namespace MenuMemoryLeak
{
    partial class TestMdiParent
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;
 
        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (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.testMenuItem = new Telerik.WinControls.UI.RadMenuItem();
            this.testParentMenu = new Telerik.WinControls.UI.RadMenu();
            ((System.ComponentModel.ISupportInitialize)(this)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.testParentMenu)).BeginInit();
            this.SuspendLayout();
            //
            // testMenuItem
            //
            this.testMenuItem.Name = "testMenuItem";
            this.testMenuItem.Text = "Parent";
            //
            // TestMdiParent
            //
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(902, 523);
            //
            // testParentMenu
            //
            this.testParentMenu.Items.AddRange(new Telerik.WinControls.RadItem[] {
            this.testMenuItem});
            this.testParentMenu.Location = new System.Drawing.Point(0, 0);
            this.testParentMenu.Name = "testParentMenu";
            this.testParentMenu.Size = new System.Drawing.Size(902, 24);
            this.testParentMenu.TabIndex = 1;
            this.testParentMenu.Text = "radMenu1";
            this.Controls.Add(this.testParentMenu);
            this.IsMdiContainer = true;
            this.Name = "TestMdiParent";
            //
            //
            //
            this.RootElement.ApplyShapeToControl = true;
            this.Text = "TestMdiParent";
            this.ThemeName = "ControlDefault";
            this.Shown += new System.EventHandler(this.TestMdiParent_Shown);
            ((System.ComponentModel.ISupportInitialize)(this.testParentMenu)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();
 
        }
 
        #endregion
 
        private Telerik.WinControls.UI.RadMenuItem testMenuItem;
        private Telerik.WinControls.UI.RadMenu testParentMenu;
 
    }
}

TestMdiChild.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Telerik.WinControls;
 
namespace MenuMemoryLeak
{
    public partial class TestMdiChild : Telerik.WinControls.UI.RadForm
    {
        public TestMdiChild()
        {
            InitializeComponent();
 
            //NOTE required for bug to occur
            this.WindowState = FormWindowState.Maximized;
        }
    }
}


TestMdiChild.Designer.cs
namespace MenuMemoryLeak
{
    partial class TestMdiChild
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;
 
        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (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.testMenuButtonItem = new Telerik.WinControls.UI.RadMenuButtonItem();
            this.testChildMenu = new Telerik.WinControls.UI.RadMenu();
            this.testTextBox = new Telerik.WinControls.UI.RadTextBox();
            ((System.ComponentModel.ISupportInitialize)(this.testChildMenu)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.testTextBox)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this)).BeginInit();
            this.SuspendLayout();
            //
            // testMenuButtonItem
            //
            this.testMenuButtonItem.Name = "testMenuButtonItem";
            this.testMenuButtonItem.Text = "Child";
            //
            // testChildMenu
            //
            this.testChildMenu.AllowMerge = false;
            this.testChildMenu.IsMainMenu = false;
            this.testChildMenu.Items.AddRange(new Telerik.WinControls.RadItem[] {
            this.testMenuButtonItem});
            this.testChildMenu.Location = new System.Drawing.Point(0, 0);
            this.testChildMenu.Name = "testChildMenu";
            this.testChildMenu.Size = new System.Drawing.Size(467, 24);
            this.testChildMenu.TabIndex = 2;
            this.testChildMenu.Text = "radMenu1";
            //
            // testTextBox
            //
            this.testTextBox.Location = new System.Drawing.Point(22, 23);
            this.testTextBox.Name = "testTextBox";
            this.testTextBox.Size = new System.Drawing.Size(100, 20);
            this.testTextBox.TabIndex = 1;
            this.testTextBox.TabStop = false;
            this.testTextBox.Text = "test";
            //
            // TestMdiChild
            //
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(467, 268);
            this.Controls.Add(this.testChildMenu);
            this.Controls.Add(this.testTextBox);
            this.Name = "TestMdiChild";
            //
            //
            //
            this.RootElement.ApplyShapeToControl = true;
            this.Text = "TestMdiChild";
            this.ThemeName = "ControlDefault";
            ((System.ComponentModel.ISupportInitialize)(this.testChildMenu)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.testTextBox)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();
 
        }
 
        #endregion
 
        private Telerik.WinControls.UI.RadMenuButtonItem testMenuButtonItem;
        private Telerik.WinControls.UI.RadMenu testChildMenu;
        private Telerik.WinControls.UI.RadTextBox testTextBox;
    }
}

If you call the parent MDI form and let it run a bit you should see the GDI objects increase by one each second and never get released.

If you know a solution/workaround, thanks a lot

Frederik

6 Answers, 1 is accepted

Sort by
0
Fred
Top achievements
Rank 1
answered on 30 Nov 2010, 11:05 AM
I'm using the latest Q3 release BTW, problem has been checked on multiple computers (win7 & winxp)
0
Richard Slade
Top achievements
Rank 2
answered on 30 Nov 2010, 01:49 PM
Hello Fred, 

I haven't been able to sort this out for you at the moment, but I am able to replicate it. As you said, without the WinDowState line, the GC seems to collect correctly. I have included a screenshot of the object retention list for the TestMdiChild from ANTS. I'll look into it further as soon as I can. 
Richard
0
Fred
Top achievements
Rank 1
answered on 30 Nov 2010, 03:13 PM
Hi Richard,

thanks a lot for confirming the leak and your memory analysis (though it only says for me the problem is somewhere in Telerik), for now it is easily worked around by by not using a button menu item but I'd be interesting to have this fixed in a future version or hear about another way to avoid it. Funny thing if you replace the textbox in the client MDI form with eg. a grid, the problem does not occur anymore, so the occurence of the leak is really tied to it being a textbox (or seems to be indirectly related to it being a textbox).

Greetings

Frederik
0
Accepted
Peter
Telerik team
answered on 02 Dec 2010, 04:34 PM
Hello Fred,

Thank you for writing.

I have updated your Telerik points for bringing this issue to our attention and I would like to thank you for the time taken to help us to investigate the case. We will do our best to address it for the upcoming SP release.

Thank you for your time.

Regards,
Peter
the Telerik team
Get started with RadControls for WinForms with numerous videos and detailed documentation.
0
Matjaž Kofol
Top achievements
Rank 2
answered on 08 Dec 2010, 11:38 PM
I am also experiencing MemoryLeaks problem with Telerik WinForms controls.
Have MDI application and on one Form in main panel I add and remove two UserControls (depending on button action). I am also attaching and removing events  to them dynamically (before Clear() is called I also remove attached events). I also called Dispose() on every Control inside my panel before calling Clear() method and then I add again the one of these two user controls to this panel.

Results:
My applications (the same form) is working slower and slower each time until it is unusable (this form and application itself).

Regards
Matjaž
0
Peter
Telerik team
answered on 11 Dec 2010, 02:54 PM
Hello Matjaž Kofol,

Thank you for writing.

Could you share with us more information (or send a sample project) about the controls you are using in this application, because the provided information is not enough for us to locate the issue.

In addition, I want to share with you that the Garbage Collector releases the instance of RadControls slower than it would free a less complex control. The reason is that RadControl is a second generation Garbage Collector object. In case you would like to release the memory instantly after the subform is closed, you should call GC.Collect() manually. More information about the garbage collection generations can be found here.

I am looking forward for your response.

Kind regards,
Peter
the Telerik team
Get started with RadControls for WinForms with numerous videos and detailed documentation.
Tags
Menu
Asked by
Fred
Top achievements
Rank 1
Answers by
Fred
Top achievements
Rank 1
Richard Slade
Top achievements
Rank 2
Peter
Telerik team
Matjaž Kofol
Top achievements
Rank 2
Share this question
or