我有一个正在制作的应用程序,它会创建大量的窗口控件(按钮和标签等)。它们都是通过功能动态制作的。我遇到的问题是,当我删除控件并处理它们时,它们不会从内存中删除。
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;
}
现在由于某种原因,这只是没有给我回忆,所以当进程运行5次时,我最终会出现内存不足的情况。我是对象和控制处置的新手,但是通过庞大的网络查看仍然没有给我任何指示,所以如果你们有任何想法,我会很高兴听到它。
更新:我一直在观察用户对象的创建和销毁(taskmanager),并注意到我创建了一个标签页,添加了一个点击处理程序,添加了一个面板,添加了两个按钮,包括点击处理程序,工具提示和背景图像(我认为这就是问题所在。)该应用程序说它创建了8个新项目,但是当我运行我的处理时,我只从内存中删除4个。我一直试图删除事件处理程序,但似乎没有任何区别。
分辨!!!当我向面板添加新项目时,我向他们传递了一个工具提示(愚蠢,但我正在学习)。对于其他遇到同样问题的人,(感谢下面的人的评论和指示。我发现为了让控件真正处理掉(因为我意识到我错误地把它放了)是:
1:如果你有工具提示,请确认它是可以访问的!不要做我想做的事! E.g:
这是错误的!
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;
}
如果你这样做,你将丢失指向工具提示的指针,并且由于工具提示不是它所连接的对象的子节点(相反,工具提示强烈引用控件),那么即使你摧毁控件,您无法访问的工具提示使对象保持活动状态。
2:在任何事情之前,调用toolTip.RemoveAll()。这将删除它与控件的所有联系。请注意,如果您将此提示用于其他控件,则他们只是丢失了他们的工具提示。
3:从基本control.ControlCollection中移除所有内部控件(如果他们使用非托管内存,我猜。我正在这样做,因为它使我的应用程序正常工作......)
4:删除所有自定义事件处理程序。
5:最后,处置对象。我做了一个快速的递归功能,可以很好地完成它。
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.MouseMove -= [Your Event];
toDispose.Dispose();
}
这非常粗糙,可能有更好的方法,但除非有人能想出来,否则必须这样做。谢谢大家的帮助。您可能刚刚保存了我的整个项目。
答案 0 :(得分:6)
您还需要清除参考文献。
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;
}
}
答案 1 :(得分:4)
在此代码块中,您正在调用Dispose但不删除引用:
while(tabControlToClear.Controls.Count > 0)
tabControlToClear.Controls[0].Dispose();
您需要删除对控件的所有引用(在Controls集合中加上任何已注册的事件处理程序以及您可能拥有的任何其他引用),以使控件有资格进行垃圾回收。
答案 2 :(得分:0)
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));
}
在我看来,您正在重新分配测试方法结束时的所有选项卡实例,因此显然没有先处理它们的好处。略过最后两行,看看是否有帮助。
答案 3 :(得分:0)
到目前为止,这个问题有很多很好的答案,提到一个可能的原因是对象没有被释放,所有这些都值得检查。
然而,当我遇到这种类型的问题时,我使用memory profiler来帮助跟踪对象的引用,上次我查看内存分析器时ANTS Memory Profiler(来自RedGate)是最好的之一。 (他们做了14天的尾巴,这个尾巴足够长,足以调查像这样的单一问题。)
这是使用弱事件模式的原因之一,但这将是whole new question。
(当您动态修改UI时,UI对象的生命周期是一个雷区,为taskmanager做好了检查,确保您的代码按预期工作)