I' currently debugging a big Winforms application that's got some memory leak issues. I use the .NET memory profiler and so far I've been able to find some of those leaks and solve them. But now I am faced with a problem which I am not sure is a problem, and if it is one I don't know how to solve it.
After running my application for like 1 min (which is not a lot considering normal users could use it for hours),.NET memory profiler shows me around 100-200 instances of different controls from the Krypton Toolkit, and this number is increasing if I keep going (they are never garbage collected, because it appears they are still being referenced somewhere). Now if I check the root path of those instances and they all look like:
I don't know where to look in my code to make these instances be de-referenced correctly when they are not needed anymore, since I don't know what is still referencing the control. I know the place where the KryptonButtonEx gets created, and from what I understand the ViewManager is created by this button, but still I don't see what I can do about it. For those who are interested, the code to create the button is this:
KryptonButton newControlButton = new KryptonButton();
newControlButton.Tag = mtActivityControl;
newControlButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
newControlButton.AutoSize = true;
newControlButton.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowOnly;
newControlButton.ButtonStyle = ComponentFactory.Krypton.Toolkit.ButtonStyle.ListItem;
newControlButton.Location = new System.Drawing.Point(3, 3);
newControlButton.Name = string.Format("controlButton{0}", mtActivityControl.SymbolicName);
newControlButton.Size = new System.Drawing.Size(96, 23);
newControlButton.StateCommon.Content.Image.ImageH = ComponentFactory.Krypton.Toolkit.PaletteRelativeAlign.Near;
newControlButton.StateCommon.Content.ShortText.TextH = ComponentFactory.Krypton.Toolkit.PaletteRelativeAlign.Near;
newControlButton.TabIndex = 5;
StringBuilder buttonText = new StringBuilder();
buttonText.Append(Path.GetFileName(mtActivityControl.ControlName));
/*if (mtActivityControl.SymbolicName.Length != 0)
{
buttonText.Append(" (");
buttonText.Append(mtActivityControl.SymbolicName);
buttonText.Append(")");
}*/
newControlButton.Text = buttonText.ToString();
newControlButton.Values.ExtraText = "";
newControlButton.Values.Image = null;
newControlButton.Values.ImageStates.ImageCheckedNormal = null;
newControlButton.Values.ImageStates.ImageCheckedPressed = null;
newControlButton.Values.ImageStates.ImageCheckedTracking = null;
newControlButton.Values.Text = buttonText.ToString();
newControlButton.Click += new System.EventHandler(this.controlsButton_Click);
and even though it is not necessary from what my research told me, I unhook the event like this in the Dispose function:
newControlButton.Click -= new System.EventHandler(this.contro开发者_开发知识库lsButton_Click);
So my question is:
Is it possible that Krypton itself keeps a reference to my controls which leads to some memory not being freed (which could be ok if it is a limited amount of memory used for keeping an object pool or something like that, but could be a problem if it is an uncontrolled memory leak)? If it doesn't come from Krypton, do you have an idea where to look to destroy those instances correctly?
Many thanks!
EDIT:
I just noticed that the class KryptonButtonEx doesn't come from Krypton but from my app. But I don't think it changes anything to the question since the only thing it does is override the GetPreferredSize function:
/// <summary>
/// An extended/fixed KryptonButton which handles resizing correctly.
/// </summary>
public class KryptonButtonEx : ComponentFactory.Krypton.Toolkit.KryptonButton
{
/// <summary>
/// Gets the size of the preferred.
/// </summary>
/// <param name="proposedSize">Size of the proposed.</param>
/// <returns></returns>
public override Size GetPreferredSize(Size proposedSize)
{
// Do we have a manager to ask for a preferred size?
if (ViewManager != null)
{
// Ask the view to peform a layout
Size retSize = ViewManager.GetPreferredSize(Renderer, proposedSize);
// Apply the maximum sizing
if (MaximumSize.Width > 0) retSize.Width = Math.Min(MaximumSize.Width, retSize.Width);
if (MaximumSize.Height > 0) retSize.Height = Math.Min(MaximumSize.Height, retSize.Width);
// Apply the minimum sizing
if (MinimumSize.Width > 0) retSize.Width = Math.Max(MinimumSize.Width, retSize.Width);
if (MinimumSize.Height > 0) retSize.Height = Math.Max(MinimumSize.Height, retSize.Height);
return retSize;
}
else
{
// Fall back on default control processing
return base.GetPreferredSize(proposedSize);
}
}
}
You posted the wrong code. Instead of the code that creates the button, you should be very interested in the code that removes it. Search for Controls.Remove and Controls.Clear and make sure that every control that is getting removed is being disposed. Another diagnostic is TaskMgr.exe, Processes tab. View + Select Columns and tick USER objects. Seeing this number go up steadily is a hard clue that the code isn't disposing controls where is should. This is the only case I know of not calling Dispose() causing a permanent leak.
Instead of Controls.Clear(), use code like this:
while (panel.Controls.Count > 0) panel.Controls[0].Dispose();
Disposing a control also automatically removes it from the parent's Controls collection.
精彩评论