Kind of. The GC will eventually free up memory, but it may not free up the memory in a timely manner. When dealing with system level objects, like bitmaps and files (and even database objects), it is better to deterministically free up the objects when you are finished with them and not rely on the GC to clean up.
Take for example the following database pseudo code...
Code:
var cn = new SqlConnection(...);
var cmd = new SqlCommand("...", cn);
var result = cmd.Execute(...);
Considering that most db providers have a 100 connection limit default, calling the above code in a tight loop will exhaust the available connections. Here's why:
It is true that the GC will recognize that the above code has gone out of scope and when the GC disposes the code, any underlying connections will be closed. The problem is that the GC doesn't run that often, so even if the code has been run and you are finished with the db resources, they are still in use while sitting around waiting for the GC to clean them up.
To take care of the problem, you can leverage the IDisposable interface and the use of the 'using' block:
Code:
using(var cn = new SqlConnection(...))
{
using(var cmd = new SqlCommand("...", cn))
{
var result = cmd.Execute(...);
} // IDisposable called on the SqlCommand object
} // IDisposable called on the SqlConnection object
In the above code, when the code reaches the end of the using block, the objects that are allocated within the using statement are freed.
I'm using a database as an example, but the same issue is true for any .net class that holds onto a handle for an underlying system object like a bitmap or a file.
So in your case, you have a form that creates a grid that contains rows of bitmaps. When you close the form, the form goes out of scope, but the bitmaps aren't cleaned up until the GC runs. You open the form and new bitmaps are created. Close and reopen the form and it doesn't take long to run out of memory.
One way to handle this problem is to alter the code to keep a list of the bitmaps around, so that you can dispose them when the form closes. You could also add an IDisposable interface to the form so you can invoke the form inside a using block. That way, when the form is closed all of its resources are immediately freed.