如果不先删除侦听器,重新分配数据源会导致内存泄漏吗?
Will reassigning datasource cause a memory leak if listeners are not removed first?
我的表单上有两个网格。 每当 一行在 grid1 中获得焦点时,其关联的子行将通过 ADO.NET 从数据库中获取,并且 grid2 的数据源将按如下方式重新分配:
//focused row changed handler
DataTable Children = FetchChildren(parentid);
grid2.DataSource = Children.DefaultView;
Children.RowDeleted += (sndr, evt) =>
{
//
};
旁白:grid1 包含很多行,所以我不想在一个(耗时的)查询中获取所有父行的子行,然后 filter客户端的大数据集子行。
当本地子变量和 grid2 的数据源在用户使用表单期间被重新分配多次时,这些匿名事件监听器会发生什么情况?不显式删除处理程序会导致内存泄漏吗?
不,在您显示的代码中不会有 内存泄漏 假设您使用该术语来指示对象不会被垃圾收集。匿名事件处理程序的委托是一个引用,一旦它超出范围,它就会与 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;
}
当我 运行 在 仪器模式 中使用 Visual Studio 中的 Performance Explorer 并使用 Collect .Net Object lifetime information 已启用这是我的结果:
如您所见,当分析结束时,所有 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
}
因此,只要您不保留对您之前使用的实例的引用,就应该没问题。
我的表单上有两个网格。 每当 一行在 grid1 中获得焦点时,其关联的子行将通过 ADO.NET 从数据库中获取,并且 grid2 的数据源将按如下方式重新分配:
//focused row changed handler
DataTable Children = FetchChildren(parentid);
grid2.DataSource = Children.DefaultView;
Children.RowDeleted += (sndr, evt) =>
{
//
};
旁白:grid1 包含很多行,所以我不想在一个(耗时的)查询中获取所有父行的子行,然后 filter客户端的大数据集子行。
当本地子变量和 grid2 的数据源在用户使用表单期间被重新分配多次时,这些匿名事件监听器会发生什么情况?不显式删除处理程序会导致内存泄漏吗?
不,在您显示的代码中不会有 内存泄漏 假设您使用该术语来指示对象不会被垃圾收集。匿名事件处理程序的委托是一个引用,一旦它超出范围,它就会与 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;
}
当我 运行 在 仪器模式 中使用 Visual Studio 中的 Performance Explorer 并使用 Collect .Net Object lifetime information 已启用这是我的结果:
如您所见,当分析结束时,所有 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
}
因此,只要您不保留对您之前使用的实例的引用,就应该没问题。