I have an application I am making that creates a large number of windows controls (buttons and labels etc). They are all being made dynamically through functions. The problem I'm having is, when I remove the controls and dispose them, they are not removed from memory.
void loadALoadOfStuff()
{
while(tabControlToClear.Controls.Count > 0)
tabControlToClear.Controls[0].Dispose();
//I even put in:
GC.Collect();
GC.WaitForPendingFinalizers();
foreach(String pagename in globalList)
tabControlToClear.Controls.Add(MakeATab(pagename));
}
TabPage MakeATab(string tabText)
{
TabPage newT = new MakeATab();
newT.Text = tabText;
//Fill page with controls with methods like this
return newT;
}
Now for some reason, this just ain't giving me my memory back, so when the process runs 5 times, I end up with an out of memory violation. I am new to object and control disposal but looking through the vast net still hasn't given me any indications, so if any of you have an idea, I'd be grateful to hear it.
UPDATE: I've been watching the user objects creation and destruction (taskmanager) and noticed I create a tab page, add a click handler, add a panel, add 2 buttons both with click handlers, tooltips and backimages (I think this is where the problem is). The app says it creates 8 new items, but when I run through my dispose, I only remove 4 from memory. I've been trying to remove the event handlers, but it seems to make no difference.
RESOLVED!!! As I was adding new items to the panel, I was passing them a tooltip (stupid, but I'm learning). For anyone else who has the same problem, (thanks to comments and directions from people below. I discovered in order to make a control truly dispose (as I realise I so incorrectly put it) is:
1: IF YOU HAVE A TOOL TIP, MAKE SURE IT'S ACCESSABLE! DO NOT DO WHAT I DID! E.g:
This is WRONG!
TabPage MakeATab(string tabText)
{
TabPage newT = new MakeATab();
ToolTip myTip = new ToolTip();
newT.Text = tabText;
//Fill page with controls with methods like this
myTip.SetToolTip(newT, "Something to say");
return newT;
}
If you do this, you will lose the pointer to the tooltip, and as the tooltip is not a child of the object it's connected to (rather, the tooltip makes a strong reference to the control), then even if you destroy the control, the tooltip that you can't access keeps the object alive.
2: Before anything, call toolTip.RemoveAll(). This removes all it's ties to controls. Note, if you were using this tip for other controls, they just lost their tool tip.
3: Remove any internal controls from the base control.ControlCollection (if they use non managed memory, I guess. I'm doing it cause it's making my app work so...)
4: remove any custom event handlers.
5: finally, dispose the object. I made a quick recursing function that does it quite well.
private void RecursiveDispose(Control toDispose)
{
while (toDispose.Controls.Count > 0)
RecursiveDispose(toDispose.Controls[0]);
if (toDispose.BackgroundImage != null)
BackgroundImage = null;
if (toDispose.GetType() == typeof(Button))
toDispose.Click -= [Your Event];
else if (toDispose.GetType() == typeof(TabPage))
toDispose.DoubleClick -= [Your Event];
else if (toDispose.GetType() == typeof(Label))
toDispose.MouseMov开发者_StackOverflowe -= [Your Event];
toDispose.Dispose();
}
This is extremely crude and there's probably a much better way of doing it, but unless someone can come up with it, this will have to do. Thanks for your help everyone. You might just have saved my entire project.
You need to also clear out the reference.
while(tabControlToClear.Controls.Count > 0)
{
var tabPage = tabControlToClear.Controls[0];
tabControlToClear.Controls.RemoveAt(0);
tabPage.Dispose();
// Clear out events.
foreach (EventHandler subscriber in tabPage.Click.GetInvocationList())
{
tabPage.Click -= subscriber;
}
}
In this code block you're calling Dispose but not removing the reference:
while(tabControlToClear.Controls.Count > 0)
tabControlToClear.Controls[0].Dispose();
You need to remove all references to the control (in the Controls collection plus any registered event handlers plus any other references you might have) for a control to be eligible for garbage collection.
void loadALoadOfStuff()
{
while(tabControlToClear.Controls.Count > 0)
tabControlToClear.Controls[0].Dispose();
//I even put in:
GC.Collect();
GC.WaitForPendingFinalizers();
foreach(String pagename in globalList)
tabControlToClear.Controls.Add(MakeATab(pagename));
}
It seems to me you are re-allocating all the tab instances at the end of your test-method, so there clearly is no benefit of disposing them first. Skip the last two lines and see if that helps.
There was been a lot of great answers to the question so far that mention one possible reason the objects are not being freed, they are all things worth checking.
However when I get this type of problem, I use a memory profiler to help track down the reference(s) to the objects, the last time I look at memory profilers, the ANTS Memory Profiler (from RedGate) was one of the best. (They do a 14 day tail that is more than long enough to investigate a single problem like this.)
This is one of the reasons that the Weak Event pattern is used, however that would be a whole new question.
(The lifetime of UI objects when you dynamically modifier an UI is a minefield, well done for taskmanager to check that your code is working as expected)
精彩评论