views:

53

answers:

2

I have memory problem in my application. Each time I fetch data from db and assign it to Grid, memory increases and sometimes when extensive searches are made the memory reaches to 1 G.B.

There can be other memory handling issues but to test that searching and binding data repeatedly consumes good amount of memory I have created a new winforms application and drop a button and a grid on it and write this code.

private void button1_Click(object sender, EventArgs e)
{
    using (SqlConnection con = new SqlConnection(connString))
    {
    SqlDataAdapter adp = new SqlDataAdapter("select * from supplier", con);
    DataSet ds = new DataSet();

    con.Open();
    adp.Fill(ds);
    con.Close();

    dataGridView1.DataSource = ds.Tables[0];                                
    ds.Dispose();
    }
}

Every time I click on this button, memory usage of this application increases, sometimes 3MB sometimes 4MB etc. There are around 2000 records in table and 12 columns of different types like datetime, VARCHAR(100), int etc.

Memory release after few minutes If I stop repeating search & bind activity.

Why memory just increases each time if amount of data is same?

I have also tried following code but no difference.

dataGridView1.DataSource = null;
dataGridView1.DataSource = ds.Tables[0]; 

I am using Telerik controls in my original application. Telerik grid consumes more memory than DataGridView, but DataGridView has same behavior.

Remember, this example is just consist of one form and a button and a grid on this form.

Thanks.

+2  A: 

ds.Dispose() will not do anything because the data is still bound to the grid. The next time in you create a new ds, fill it and then bind it, the old ds values are no longer bound, but the garbage collector won't necessarily immediately clean up. Each time you click, you consume more memory without releasing any. The memory eventually frees because when you stop the repeated search and binding, the GC gets a chance to cleanup the old un-bound data.

While the approximate size of the data for each search should be the same, to the new'd ds and the datagridview, each time through is a unique event and there is no correlation to the data from the prior search/bind.

I think, but definitely not sure, that the GC is designed to not be performance impacting, that is, it shouldn't take over the CPU to do collection unless there are significant memory issues. It should try to do clean up during idle CPU time. In that case all the searching and binding effectively puts the GC on hold.

cdkMoose
+1  A: 

DataSet is a heavy object to be using just to pull data from a database. It only merits use if you need to have multiple tables with relations, etc. Think of it as an in-memory database that can sync to your permanent database.

One option is to use a custom object and a DataReader to loop through and fill a List<yourObject>. This will likely consume the least amount of memory.

Another option which keeps it closer to what you have, is to skip the DataSet. Try something like this and see if memory usage improves:

using (SqlConnection myConnection = new SqlConnection(connectionString))
using (SqlCommand myCommand = new SqlCommand("select * from supplier", myConnection))
{
  myConnection.Open();

  using (SqlDataReader myReader = myCommand.ExecuteReader())
  {
    DataTable myTable = new DataTable();
    myTable.Load(myReader);
    myConnection.Close();

    dataGridView1.DataSource = myTable;
  }
}

Side note: Currently in your code, if an exception occurs between DataSet ds = new DataSet() and ds.Dispose(), the Dispose() will not run. You should be doing using (DataSet ds = new DataSet()) { ... } to guarantee that Dispose() will run. You can then remove the ds.Dispose().

Nelson
@cdkMoose gave more the reasons why it happens and why it can't be avoided (GC runs when it wants to run). My answer is on how to use more lightweight (less overhead) objects so that it runs faster and minimizes the memory usage. If it's server-side code it will also be more scalable.
Nelson