Will reassigning a data source cause a memory leak if listeners are not removed first?

I have two grids in my form. Whenever a row gets focus in grid1, the child rows associated with it are retrieved from the database through ADO.NET, and grid2 DataSource is reassigned as follows:

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

ASIDE: grid1 contains many rows, so I donโ€™t want to get child rows for all parent rows in one (time-consuming) query and then filter a large set of data from child rows on the client side.

What happens to such anonymous eventlisteners when the local variable Children and the grid2 data source are repeatedly reassigned during the user working with the form? Is it not obvious that deleting handlers causes a memory leak?

+5
source share
1 answer

No, there will be no memory leak in the code you show, assuming you use this term to indicate that objects are not garbage collected. The delegate for an anonymous event handler is a link that receives garbage collected for a long time with the rest of the objects created by the DataTable when it goes out of scope.

I created the following test setup to simulate what your code does:

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

When I run this using the Performance Explorer in Visual Studio in Instrumentaion mode and with Collect.Net Object's life information enabled, this is my result:

Object lifetime

As you can see, all DataTable instances return to 0 when profiling ends, and this applies to all types that were created in this test.

If you keep the links, but at the end of the method you can get 1000 instances, as shown in this test file:

 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 } 

So, until you keep reference to the instance you used earlier, you should be fine.

+1
source

Source: https://habr.com/ru/post/1237276/


All Articles