如果Windows 窗体应用程序使用 ToolTip.SetToolTip,则内存未释放

本文可帮助你解决以下问题:如果方法不正确,Windows 窗体应用程序可能无法释放内存ToolTip.SetToolTip

原始产品版本: .NET Framework 4.5
原始 KB 数: 2749543

症状

你开发了一个 .NET Windows 窗体应用程序,该应用程序使用ToolTip.SetToolTip该方法将工具提示控件与Windows 窗体控件相关联。 以编程方式从窗体中删除关联的Windows 窗体控件并释放控件时,该控件不会从托管堆中释放。 随着时间的推移,如果反复添加和删除与 ToolTip 使用 ToolTip.SetToolTip关联的控件,内存使用率会继续增加,最终可能会导致一个 System.OutOfMemoryException

原因

调用ToolTip.SetToolTip以与Windows 窗体控件关联ToolTip时,对象ToolTip会将内部信息(包括对控件的引用)存储在内部 HashTable 中。 如果使用单个工具提示控件并将其与许多控件相关联,则每个控件及其 ToolTip 信息的引用将存储在 HashTable 中。 既不调用控件 Dispose 的方法,也不从 Controls 容器的集合中删除控件与控件 ToolTip的关联。 因此,在应用程序动态将控件添加到窗体、将它们与一个 ToolTip控件相关联以及删除和释放控件的情况下,该控件仍植根于内存中,并且应用程序的托管内存堆会随着时间的推移而继续增长。

决议

根据应用程序设计,有多种方法可以解决此问题。

如果应用程序对许多Windows 窗体控件使用一个工具提示控件,则可以通过调用ToolTip.SetToolTip和传递对控件的引用和标题的ToolTip空字符串,将特定Windows 窗体控件与工具提示取消关联。 为ToolTip标题传递空字符串时,该方法SetToolTip将从内部 HashTable ToolTip中删除对 Windows 窗体 控件的引用。

注释

还可以调用ToolTip.RemoveAll删除所有ToolTip文本并将工具提示控件与所有Windows 窗体控件取消关联。

如果应用程序未将多个Windows 窗体控件与单个工具提示相关联,则可以调用Dispose该方法ToolTip

详细信息

此行为是设计造成的。

请考虑Windows 窗体应用程序中的以下示例代码。 该方法中的 CreateControls() 代码会创建多个 TextBox 控件,调用 ToolTip.SetToolTip 将标题分配给 ToolTip 每个控件,并将控件添加到内部 ObservableCollection控件和 Controls 集合中。 该方法中的 RemoveControls() 代码遍历 ObservableCollection每个控件,获取对每个控件的引用,然后从 ObservableCollection 窗体的 Controls 集合中删除它,然后调用其 Dispose 方法。 这会导致从窗体中删除并释放每个 TextBox 控件,但每个 TextBox 和关联的 ToolTipInfo 实例仍植根于托管堆中。 取消注释方法中的 RemoveControls() 指定行将 TextBox 与 ToolTip TextBox 取消关联,并允许对其进行垃圾回收。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections.ObjectModel;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        ObservableCollection<TextBox> list;
        ToolTip toolTip;
        public Form1()
        {
            InitializeComponent();
            toolTip = new ToolTip();
            list = new ObservableCollection<TextBox>();
        }
        private void button1_Click(object sender, EventArgs e)
        {
            CreateControls();
        }
        private void button2_Click(object sender, EventArgs e)
        {
            RemoveControls();
        }
        private void CreateControls()
        {
            for (int i = 0; i < 100; i++)
            {
                TextBox t = new TextBox();
                toolTip.SetToolTip(t, i.ToString());
                t.Left = (t.Width * i) + 5;
                list.Add(t);
                this.Controls.Add(t);
            }
        }
        private void RemoveControls()
        {
            for (int i = list.Count-1;i>=0;i--)
            {
                TextBox tb = list[i];
                this.Controls.Remove(tb);
                list.Remove(tb);
                //Uncomment this line to disassociate the ToolTip control from the Windows Forms Control
                //toolTip.SetToolTip(tb, "");
                tb.Dispose();
            }
        }
    }
}