I've recently been some refactoring some old database access code. I have a library of hundreds of methods which look something like this
public int getFoo(int id)
{
using(SqlConnection connection = ConnectionManager.GetConnection())
{
string sql = "SELECT TOP(1) foo FROM bar WHERE id=@id";
SqlCommand command = new SqlCommand(sql, connection);
command.Parameters.AddWithValue("@id", id);
return (int)command.ExecuteScalar();
}
}
I figured it would be a sensible thing to wrap the SqlCommand
s into a using{}
block (like the SqlConnection
already is) so that resources get disposed of as soon as possible. Out of intellectual curiosity I decided to make the following little console application to see how much memory would be freed up:
using (SqlConnection conn = ConnectionManager.GetConnection())
{
WeakReference reference;
string sql = "SELECT COUNT(foo) FROM bar";
Console.WriteLine("Memory Allocated before SqlCommand: " + GC.GetTotalMemory(true));
using (SqlCommand comm = new SqlCommand(sql,conn))
{
Console.WriteLine("Memory Allocated after SqlCommand: " + GC.GetTotalMemory(true));
reference = new WeakReference(comm);
Console.WriteLine("SQL output: " + comm.ExecuteScalar());
Console.WriteLine(
"Memory Allocated before dispose SqlCommand: " + GC.GetTotalMemory(true));
}
GC.Collect();
Console.WriteLine("Memory Allocated after SqlCommand: " + GC.GetTotalMemory(true));
Console.WriteLine("Reference is alive: " + reference.IsAlive);
Console.ReadLine();
}
To my surprise this is the output I got:
Memory Allocated before SqlCommand: 236384
Memory Allocated after SqlCommand: 239160
SQL output: (whatever)
Memory Allocated before dispose SqlCommand: 246416
Memory Allocated after dispose SqlCommand: 246548 <-- It's gone up!?
Reference is alive: True <-- Why is reference still alive?
At first I though maybe my WeakReference
might be somehow keeping command
alive, but I commented that code out and still I got a similar result.
Why is command
not being garbage collected here, even when GC.Collect()
has been explicitly called? If a variable is introduced in a using block when can we expect that variable to be eligible for garbage collection?
Disposing of objects has nothing to do with garbage collection. Garbage collection has to do with cleaning up of managed resources. Disposal is there to clean up unmanaged resources that the GC isn't tracking.
Sometimes those unmanaged resources are memory allocated explicitly without going through the GC, sometimes they're locks on a file handle, a database connection, a network socket, or any number of other possibilities. But whatever they are, they very explicitly won't be the memory that the GC is tracking, which is what you're measuring.
As far as accounting for the differences, your differences are simply within the noise level of the program. Your changes aren't meaningfully affecting how much managed memory is used, and the differences you're seeing are consistent with the normal fluctuation of memory in a program that uses garbage collection.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With