7

我正在制作一个创建大量窗口控件(按钮和标签等)的应用程序。它们都是通过函数动态生成的。我遇到的问题是,当我删除控件并处理它们时,它们并没有从内存中删除。

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 次时,我最终会出现内存不足的情况。我是对象和控制处置的新手,但是通过庞大的网络查看仍然没有给我任何迹象,所以如果你们中的任何人有想法,我会很感激听到它。

更新:我一直在观察用户对象的创建和销毁(任务管理器),并注意到我创建了一个标签页,添加了一个点击处理程序,添加了一个面板,添加了 2 个带有点击处理程序、工具提示和背景图像的按钮(我认为这是问题是)。该应用程序说它创建了 8 个新项目,但是当我运行我的处置时,我只从内存中删除了 4 个。我一直在尝试删除事件处理程序,但似乎没有什么区别。

解决!!!当我向面板添加新项目时,我向他们传递了一个工具提示(愚蠢,但我正在学习)。对于其他有同样问题的人,(感谢下面人们的评论和指示。我发现为了使控件真正处置(我意识到我说错了)是:

1:如果您有工具提示,请确保它可以访问!不要做我做过的事!例如:

这是错误的!

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();
    }

这是非常粗糙的,可能有更好的方法来做,但除非有人能想出它,否则必须这样做。谢谢大家的帮助。您可能刚刚保存了我的整个项目。

4

4 回答 4

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;
    }
}
于 2010-01-12T06:09:19.457 回答
4

在此代码块中,您正在调用 Dispose 但未删除引用:

while(tabControlToClear.Controls.Count > 0)
    tabControlToClear.Controls[0].Dispose();

您需要删除对控件的所有引用(在 Controls 集合中加上任何已注册的事件处理程序以及您可能拥有的任何其他引用),才能使控件有资格进行垃圾回收。

于 2010-01-12T06:08:32.687 回答
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));
}

在我看来,您在测试方法结束时重新分配了所有选项卡实例,因此首先处理它们显然没有任何好处。跳过最后两行,看看是否有帮助。

于 2010-01-12T06:08:58.360 回答
0

到目前为止,这个问题有很多很好的答案,其中提到了对象没有被释放的一个可能原因,它们都是值得检查的东西。

但是,当我遇到此类问题时,我使用内存分析器来帮助跟踪对对象的引用,上次我查看内存分析器时,ANTS 内存分析器(来自RedGate)是最好的之一。(他们做了一个 14 天的尾巴,足以调查这样的一个问题。)

这是使用弱事件模式的原因之一,但这将是一个全新的问题

(当您动态修改 UI 时,UI 对象的生命周期是一个雷区,任务管理器可以很好地检查您的代码是否按预期工作)

于 2010-01-12T09:22:41.640 回答