Reputation: 2179
I have a timer in C# which executes some code inside it's method. Inside the code I'm using several temporary objects.
If I have something like Foo o = new Foo();
inside the method, does that mean that each time the timer ticks, I'm creating a new object and a new reference to that object?
If I have string foo = null
and then I just put something temporal in foo, is it the same as above?
Does the garbage collector ever delete the object and the reference or objects are continually created and stay in memory?
If I just declare Foo o;
and not point it to any instance, isn't that disposed when the method ends?
If I want to ensure that everything is deleted, what is the best way of doing it:
Foo o;
outside the timer's method and just make the assignment o = new Foo()
inside, so then the pointer to the object is deleted after the method ends, the garbage collector will delete the object.Upvotes: 51
Views: 166507
Reputation: 50825
The .NET garbage collector takes care of all this for you.
It is able to determine when objects are no longer referenced and will (eventually) free the memory that had been allocated to them.
Upvotes: 18
Reputation: 48949
1.If I have something like Foo o = new Foo(); inside the method, does that mean that each time the timer ticks, I'm creating a new object and a new reference to that object?
Yes.
2.If I have string foo = null and then I just put something temporal in foo, is it the same as above?
If you are asking if the behavior is the same then yes.
3.Does the garbage collector ever delete the object and the reference or objects are continually created and stay in memory?
The memory used by those objects is most certainly collected after the references are deemed to be unused.
4.If I just declare Foo o; and not point it to any instance, isn't that disposed when the method ends?
No, since no object was created then there is no object to collect (dispose is not the right word).
5.If I want to ensure that everything is deleted, what is the best way of doing it
If the object's class implements IDisposable
then you certainly want to greedily call Dispose
as soon as possible. The using
keyword makes this easier because it calls Dispose
automatically in an exception-safe way.
Other than that there really is nothing else you need to do except to stop using the object. If the reference is a local variable then when it goes out of scope it will be eligible for collection.1 If it is a class level variable then you may need to assign null
to it to make it eligible before the containing class is eligible.
1This is technically incorrect (or at least a little misleading). An object can be eligible for collection long before it goes out of scope. The CLR is optimized to collect memory when it detects that a reference is no longer used. In extreme cases the CLR can collect an object even while one of its methods is still executing!
Update:
Here is an example that demonstrates that the GC will collect objects even though they may still be in-scope. You have to compile a Release build and run this outside of the debugger.
static void Main(string[] args)
{
Console.WriteLine("Before allocation");
var bo = new BigObject();
Console.WriteLine("After allocation");
bo.SomeMethod();
Console.ReadLine();
// The object is technically in-scope here which means it must still be rooted.
}
private class BigObject
{
private byte[] LotsOfMemory = new byte[Int32.MaxValue / 4];
public BigObject()
{
Console.WriteLine("BigObject()");
}
~BigObject()
{
Console.WriteLine("~BigObject()");
}
public void SomeMethod()
{
Console.WriteLine("Begin SomeMethod");
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("End SomeMethod");
}
}
On my machine the finalizer is run while SomeMethod
is still executing!
Upvotes: 50
Reputation: 7452
As Brian points out the GC can collect anything that is unreachable including objects that are still in scope and even while instance methods of those objects are still executing. consider the following code:
class foo
{
static int liveFooInstances;
public foo()
{
Interlocked.Increment(ref foo.liveFooInstances);
}
public void TestMethod()
{
Console.WriteLine("entering method");
while (Interlocked.CompareExchange(ref foo.liveFooInstances, 1, 1) == 1)
{
Console.WriteLine("running GC.Collect");
GC.Collect();
GC.WaitForPendingFinalizers();
}
Console.WriteLine("exiting method");
}
~foo()
{
Console.WriteLine("in ~foo");
Interlocked.Decrement(ref foo.liveFooInstances);
}
}
class Program
{
static void Main(string[] args)
{
foo aFoo = new foo();
aFoo.TestMethod();
//Console.WriteLine(aFoo.ToString()); // if this line is uncommented TestMethod will never return
}
}
if run with a debug build, with the debugger attached, or with the specified line uncommented TestMethod will never return. But running without a debugger attached TestMethod will return.
Upvotes: 1
Reputation: 1756
Here's a quick overview:
One last thing: If you declare Foo foo;
without assigning it you don't have to worry - nothing is leaked. If Foo is a reference type, nothing was created. If Foo is a value type, it is allocated on the stack and thus will automatically be cleaned up.
Upvotes: 3
Reputation: 1348
Let's answer your questions one by one.
Upvotes: 3
Reputation: 8595
Objects are eligable for garbage collection once they go out of scope become unreachable (thanks ben!). The memory won't be freed unless the garbage collector believes you are running out of memory.
For managed resources, the garbage collector will know when this is, and you don't need to do anything.
For unmanaged resources (such as connections to databases or opened files) the garbage collector has no way of knowing how much memory they are consuming, and that is why you need to free them manually (using dispose, or much better still the using block)
If objects are not being freed, either you have plenty of memory left and there is no need, or you are maintaining a reference to them in your application, and therefore the garbage collector will not free them (in case you actually use this reference you maintained)
Upvotes: 9
Reputation: 2783
The garbage collector will come around and clean up anything that no longer has references to it. Unless you have unmanaged resources inside Foo
, calling Dispose or using a using statement on it won't really help you much.
I'm fairly sure this applies, since it was still in C#. But, I took a game design course using XNA and we spent some time talking about the garbage collector for C#. Garbage collecting is expensive, since you have to check if you have any references to the object you want to collect. So, the GC tries to put this off as long as possible. So, as long as you weren't running out of physical memory when your program went to 700MB, it might just be the GC being lazy and not worrying about it yet.
But, if you just use Foo o
outside the loop and create a o = new Foo()
each time around, it should all work out fine.
Upvotes: 1
Reputation: 41209
Foo
variable will never ever take up memory.using
statement simply calls dispose on an IDisposable
object when it exits, so this is equivalent to your second bullet point. Both will indicate that you are done with the object and tell the GC that you are ready to let go of it. Overwriting the only reference to the object will have a similar effect.Upvotes: 2