0

I have an asp.net website that is being hosted in IIS. I have recently noticed that with large result sets being returned from the database, that the memory for IIS Worker Process just keeps growing (about 400MB each time the query is run). If a few of these large queries happen to be run at the same time it can just eat away at the memory (have seen it reach 5GB) and the server slows right down.

I have narrowed it down to a single line of code, when the data is loaded into the DataTable.

using (SqlConnection connection = new SqlConnection(connectionString))
using (SqlCommand command = new SqlCommand(storedProcedureName, connection))
using(DataTable dataTable = new DataTable())
{
    command.CommandType = System.Data.CommandType.StoredProcedure;
    connection.Open();

    using (SqlDataReader reader = command.ExecuteReader())
    {
        // Memory Spikes on dataTable.Load
        dataTable.Load(reader);
    }
}

What I dont understand is that the memory being allocated to the DataTable doesn't seem to be disposed of as I would expect. When the DataTable falls out of scope or when the webpage is navigated away from or even when the user logs out of the site, the memory stays at the same level. This is obviously an issue in a multi user system.

I have used a memory profiler and it is holding thousands of strings in memory which are the results of the query held in the DataTable, but I am not really sure where to go from here? Am I misunderstanding how I should be handling this?

IanSoc
  • 238
  • 1
  • 3
  • 14
  • 1
    https://stackoverflow.com/questions/913228/should-i-dispose-dataset-and-datatable/1603516#1603516 – user6144226 Aug 03 '17 at 17:07
  • 400 MB in memory after only one query? Did you consider to minimize, the dataset taken from DB ? – Piotr Aug 03 '17 at 20:15
  • Piotr, unfortunately it is not immediately possible, it is a somewhat legacy system with a wide range of reports that are all designed in the same way. – IanSoc Aug 04 '17 at 09:47

1 Answers1

0

It's not the issue. It's how Garbage Collector works. When you dispose of an object it is not removed from memory right away. It's just marked as ready to be disposed of for garbage collector.

This is a quote from MS Exams book

The stack is automatically cleared at the end of a method. The CLR takes care of this and you don’t have to worry about it. The heap is another story—it is managed by the garbage collector. In unmanaged environments without a garbage collector, you have to keep track of which objects were allocated on the heap and you need to free them explicitly. In the .NET Framework, this is done by the garbage collector.

The garbage collector works with a mark and compact algorithm. The mark phase of a collection checks which items on the heap are still being referenced by a root item. A root can be a static field, a method parameter, a local variable, or a CPU register. If the garbage collector finds a “living” item on the heap, it marks the item. After checking the whole heap, the compact operation starts. The garbage collector then moves all living heap objects close together and frees the memory for all other objects. For doing this, the garbage collector has to make sure that no state is changing while performing all the marking and compacting. Because of this, all threads are frozen while doing a collect operation. It also has to make sure that all references to living objects are correct. After moving objects around, the garbage collector will fix all existing references to objects.

You can try to force garbage collector to perform cleaning using:

GC.Collect(); 
GC.WaitForPendingFinalizers(); 

Moreover, GC uses multiple generations and only Generation 0 is easy to clean-up and GC clean it up first. Only then GC decided that it cannot release enough memory it will start to process the other generations. And moving to these generations could create latency.

UPDATE: You can try also divide your big dataset to small chunks and retrieve them accordingly.

Community
  • 1
  • 1
SouXin
  • 1,565
  • 11
  • 17
  • Hi SouXin, thanks for your answer. While calling the garbage collector directly does clear the memory, I'm still a bit unsure of why it isn't being collected automatically. Am I worrying about nothing and it will in fact be collected when it needs it? The reason I am concerned about this is because this is happening in a multi user system which also have multiple reports and I have seen the memory allocated to IIS reach 5GB. Should I not be worried about this? – IanSoc Aug 04 '17 at 09:00
  • If GC decides your data-table sits on Generation 1 or 2, yes you better try to change the logic. If it in generation 0. Don't worry. What you can do although just overwrite Dispose method for data-table, so you could be sure it's gonna be clean up automatically right away. BTW it does collect it automatically but after some amount of time. – SouXin Aug 04 '17 at 13:29