c# - 如果不先删除监听器,重新分配数据源会导致内存泄漏吗?

标签 c# winforms ado.net

我的表单上有两个网格。 每当一行在 grid1 中获得焦点时,其关联的子行将通过 ADO.NET 从数据库中获取,并且 grid2 的数据源将按如下方式重新分配:

     //focused row changed handler
     DataTable Children = FetchChildren(parentid);
     grid2.DataSource = Children.DefaultView;
     Children.RowDeleted += (sndr, evt) =>
      {
        //
      };

旁白:grid1 包含很多行,所以我不想在一个(耗时的)查询中获取所有父行的子行,然后过滤大的子数据集行客户端。

当本地子变量和 grid2 的数据源在用户使用表单期间被重新分配多次时,这些匿名事件监听器会发生什么情况?不显式删除处理程序会导致内存泄漏吗?

最佳答案

不,在您显示的代码中不会有内存泄漏 假设您使用该术语来指示对象不会被垃圾收集。匿名事件处理程序的委托(delegate)是一个引用,一旦超出范围,它就会与 DataTable 创建的其余对象一起进行垃圾回收。

我已经创建了以下测试装置来模拟您的代码正在做什么:

static object DataSource;

static void Main(string[] args)
{
    Test1();
    // clean up
    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);
    GC.WaitForPendingFinalizers();
    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);
}

static void Test1()
{
    for (var i = 0; i < 1000; i++)
    {
        var dt = FetchChildren(i);
        DataSource = dt.DefaultView;
        dt.RowDeleted += (s, e) =>
        {
            var table = (DataTable)s;
            Trace.WriteLine(String.Format("{0}:{1}:{2}", e.Action, e.Row.RowState, table));
        };
        // do work
        var dv = (DataView)DataSource;
        dv.Delete(5);
    }
    DataSource = null;
}

// create a useful datatable
static DataTable FetchChildren(int parent)
{
    var dt = new DataTable();
    dt.Columns.Add("key", typeof(int));
    dt.Columns.Add("guid", typeof(string));

    for(var i=0; i<10; i++)
    {
        var row = dt.NewRow();
        row[0] = parent;
        row[1] = Guid.NewGuid().ToString("N");
        dt.Rows.Add(row);
    }

    return dt;
}

当我在 Instrumentaion 下使用 Visual Studio 中的性能资源管理器运行它并启用收集 .Net 对象生命周期信息时,这是我的结果:

Object lifetime

如您所见,当分析结束时,所有 DataTable 实例都回到 0,这适用于该测试中实例化的所有类型。

如果你保留引用,但是你可以在方法的末尾得到 1000 个实例,如这个测试用例所示:

static void Test2()
{
    for (var i = 0; i < 1000; i++)
    {
        var dt = FetchChildren(i);
        var local = DataSource; // our previous DataTable
        dt.RowDeleted += (s, e) =>
        {
            var table = (DataTable)s;
            Trace.WriteLine(String.Format("{0}:{1}:{2}", e.Action, e.Row.RowState, local)); // use it here
        };
        DataSource = dt.DefaultView;
        // do work
        var dv = (DataView)DataSource;
        dv.Delete(5);
    }
    //DataSource = null; // don't dereference
}

因此,只要您不保留对您之前使用的实例的引用,就应该没问题。

关于c# - 如果不先删除监听器,重新分配数据源会导致内存泄漏吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34050418/

相关文章:

c# - SqlDataReader 没有返回所有行

c# - 在代码隐藏中从 XAML 创建控件?

c# - 检测数据集/数据表中的行何时更改

.net - 您能否为 .NET Winforms 应用程序推荐低成本的自动化测试工具?

.net - 免费的 .NET Windows 控件库?

c# - SQL + Cyrillic 支持 + 参数化查询 + 多项选择

c# - Telerik RadTabStrip 的问题

c# - 选中的列表框要求我选择一个项目两次

c# - Linux 中的 Mono Winforms 应用程序 : some text is not displayed

c# - 带有 ADO.Net 示例的 Asp.Net 核心应用程序