"using" vs "= null" and object lifetime

M

MuZZy

Hi,

Lately i've been (and still am) fixing some memory leaks problems in the project i just took over
when i got this new job. Among the other issues i've noticed that for localy created objects it
makes difference to explicitly put them to null after working with them is done - it somehow makes
the GC collect them sooner, like here:

void SomeFunc()
{
MyClass c = new MyClass();
c.CallSomeOtherFunc();
c = null;
}

If i don't put "c" to null time to get it collected is way longer than if i explicitly dereference
the object. It's really strange, as it's obvoius that object's lifetime is limited by body of
"SomeFunc". But anyway, my question is: is there any difference in the first example compared to this:

void SomeFunc()
{
using (MyClass c = new MyClass())
{
c.CallSomeOtherFunc();
}
}

The second way looks nicer for me and avoids forgetting to explicitly dereference the object,
but i'm not sure if this second case is being treated the same way as the first one.

Any suggestions/ideas would be highly appreciated!

Thnak you,
Andrey
 
T

Tom Porterfield

Lately i've been (and still am) fixing some memory leaks problems in the project i just took over
when i got this new job. Among the other issues i've noticed that for localy created objects it
makes difference to explicitly put them to null after working with them is done - it somehow makes
the GC collect them sooner, like here:

void SomeFunc()
{
MyClass c = new MyClass();
c.CallSomeOtherFunc();
c = null;
}

If i don't put "c" to null time to get it collected is way longer than if i explicitly dereference
the object. It's really strange, as it's obvoius that object's lifetime is limited by body of
"SomeFunc". But anyway, my question is: is there any difference in the first example compared to this:

void SomeFunc()
{
using (MyClass c = new MyClass())
{
c.CallSomeOtherFunc();
}
}

The second way looks nicer for me and avoids forgetting to explicitly dereference the object,
but i'm not sure if this second case is being treated the same way as the first one.

Your second case is not the same as the first. Another way to write the
second would be:

void SomeFunc()
{
MyClass c = new MyClass()
try
{
c.CallSomeOtherFunc();
}
finally
{
c.Dispose();
}
}

So in your first example you are setting c to null before it goes out of
scope. In your second example you are not setting c to null, but you are
guaranteeing that c.Dispose() will be called no matter what happens.
 
N

Nicholas Paldino [.NET/C# MVP]

Andrey,

See inline:
Lately i've been (and still am) fixing some memory leaks problems in the
project i just took over when i got this new job. Among the other issues
i've noticed that for localy created objects it makes difference to
explicitly put them to null after working with them is done - it somehow
makes the GC collect them sooner, like here:

void SomeFunc()
{
MyClass c = new MyClass();
c.CallSomeOtherFunc();
c = null;
}

This depends on how it is compiled. If you are compiling in debug mode,
then yes, it will make it GC faster (although how much faster is
questionable since c would have been released very quickly afterwards, due
to exiting the method). The reason for this is that code compiled in debug
mode will not have references released when they are no longer used. When
compiled for release mode, however, you are actually ^extending^ the
lifetime of your object with the "c = null" assignment. If the statement
was not there, the compiler would realize that the object pointed to by c is
no longer needed after the call to CallSomeOtherFunc (unless the reference
pointed to by c is stored somewhere else in the call to CallSomeOtherFunc),
and then set it to null, allowing it to become eligible for GC.

However, with the "c = null" statement, the compiler realizes (although
I don't know if agressive optimizations here can pick it up) that it is
needed for another call, and the object pointed to by c isn't made eligible
until ^after^ the assignment.
If i don't put "c" to null time to get it collected is way longer than if
i explicitly dereference the object. It's really strange, as it's obvoius
that object's lifetime is limited by body of "SomeFunc".

This isn't true. GCs are not pre-determined. Why a GC occurs very
quickly in one run of the app as opposed to very late in another run of the
app is not easily determinable. There are other factors outside your
application which can force a GC as well. The timing of one GC in one run
of the app vs a GC of another run in an app means very little.
But anyway, my question is: is there any difference in the first example
compared to this:

void SomeFunc()
{
using (MyClass c = new MyClass())
{
c.CallSomeOtherFunc();
}
}

The second way looks nicer for me and avoids forgetting to explicitly
dereference the object,
but i'm not sure if this second case is being treated the same way as the
first one.

This is a separate issue completely. Just because you implement
IDispose, it doesn't mean that the object doesn't need to be GCed anymore.
If you have IDispose, it means that you need to manage the lifetime of the
object in some way by calling the Dispose method on the implementation of
IDispose. There are usually two reasons you implement IDispose.

The first, and most common, is because the instance is holding on to
some unmanaged resource, and you need to release it as soon as possible.
When implementing IDispose in this scenario, you also need to implement a
finalizer (destructor, although the term is incorrect, IMO). The finalizer
is meant to be a safeguard if people don't call Dispose. It will basically
release the unmanaged resource if it hasn't been done already.

Finalization is an expensive process. The object is basically
ressurected and placed in a finalization queue, and then has the finalizer
executed. If Dispose is called on an object, it doesn't need to be
finalized, which is why you see a call to GC.SuppressFinalize in the
implementation of Dispose, and not the finalizer itself. Since the
finalizer and Dispose do the same thing, if Dispose is called, you can
effectively tell the GC to not finalize you, and avoid that costly overhead.

However, even if you say that you are going to suppress finalization,
the object still has to be GCed. The process of having the memory reclaimed
still has to occur on this object, even when Dispose is called. However,
it's something you really don't care about, because it's just memory, and
you gave up control of that (for the most part) by agreeing to run in the
CLR (that's the purpose of the GC, to worry about these things, not yours).

Suppressing finalization only means the finalizer will not be called, it
doesn't mean that the object has been GCed.

The second reason to use IDispose is because you need some deterministic
finalization to take place. For example, you might always need some
variable set back after an operation in a method. In this case generating a
class which implements IDisposable might be a good idea, since you can
revert the variable back when the using block is exited (instead of having
to explicitly remembering to code a try/catch/finally block).

In your case, you fall into the first category for using IDispose.
Using the using statement will be important, if you actually have an
unmanaged resource you are holding on to. If you do not, and it is just a
regular class, then using the using statement will not work, and
implementing IDispose will not help either.

Hope this helps.
 
M

MuZZy

Tom said:
Your second case is not the same as the first. Another way to write the
second would be:

void SomeFunc()
{
MyClass c = new MyClass()
try
{
c.CallSomeOtherFunc();
}
finally
{
c.Dispose();
}
}

So in your first example you are setting c to null before it goes out of
scope. In your second example you are not setting c to null, but you are
guaranteeing that c.Dispose() will be called no matter what happens.

Thank you!
So if i'm more concerned about shorter object lifetime, i'd rather do this:

void SomeFunc()
{
MyClass c = new MyClass()
c.CallSomeOtherFunc();
c.Dispose();
c = null;
}

than use "using"?
Right?
 
S

Sherif ElMetainy

Hello

In a Debug build, the object will not be eligible for garbage collection
until the function returns or you set the object reference to null. In a
Release build, the JIT compiler makes some optimizations, so that the
variable is eligible for garbage collection as soon as it is no longer
needed, even if the function call is still active. So in a release build you
don't need to set the object reference to null after you finish using it
because CLR and JIT will take care of that.

As for the using statement, its use is not to make GC collect the memory for
the object. It is used so that any resource (such as db connections, file
handles, GDI object, native memory, etc) held by the object is released
soon, even if the GC collects it at a later time. It can be used only with
objects that implement IDisposable, and it makes sure that the Dispose
method gets called after you finish using the object.

Best regards,
Sherif

MuZZy said:
Hi,

Lately i've been (and still am) fixing some memory leaks problems in the project i just took over
when i got this new job. Among the other issues i've noticed that for localy created objects it
makes difference to explicitly put them to null after working with them is done - it somehow makes
the GC collect them sooner, like here:

void SomeFunc()
{
MyClass c = new MyClass();
c.CallSomeOtherFunc();
c = null;
}

If i don't put "c" to null time to get it collected is way longer than if i explicitly dereference
the object. It's really strange, as it's obvoius that object's lifetime is limited by body of
"SomeFunc". But anyway, my question is: is there any difference in the
first example compared to this:
 
M

MuZZy

Thanks guys, now it's more clear for me!
MOst important things you've told are that it makes sence to use "using" only if object has
IDisposable interface, and second, that debug mode slighty differs from Release mode in terms of
memory management - i shouldn't explcitly dereference objects in Release mode.
 
T

Tom Porterfield

So if i'm more concerned about shorter object lifetime, i'd rather do this:

void SomeFunc()
{
MyClass c = new MyClass()
c.CallSomeOtherFunc();
c.Dispose();
c = null;
}

than use "using"?
Right?

No, see Nicholas' reply on why this doesn't guarantee a shorter object
lifetime, and in fact might lengthen it.
 
J

Jon Skeet [C# MVP]

MuZZy said:
Lately i've been (and still am) fixing some memory leaks problems in
the project i just took over when i got this new job. Among the other
issues i've noticed that for localy created objects it makes
difference to explicitly put them to null after working with them is
done - it somehow makes the GC collect them sooner, like here:

void SomeFunc()
{
MyClass c = new MyClass();
c.CallSomeOtherFunc();
c = null;
}

If i don't put "c" to null time to get it collected is way longer
than if i explicitly dereference the object.

In release, or only in debug? I'd be very surprised if it made any
difference in release mode.
 
J

Jon Skeet [C# MVP]

Nicholas Paldino said:
This depends on how it is compiled. If you are compiling in debug mode,
then yes, it will make it GC faster (although how much faster is
questionable since c would have been released very quickly afterwards, due
to exiting the method). The reason for this is that code compiled in debug
mode will not have references released when they are no longer used.

Are you sure it's a case of how it's compiled? I was under the
impression that it was how it was being *run* - which would make sense,
as if you're not running in the debugger, you don't need to know the
value of c after the last use even if the debug symbols are present.

When I get the chance I may test this in each configuration just to see
what happens...
 
S

Sean Hederman

MuZZy said:
Thanks guys, now it's more clear for me!
MOst important things you've told are that it makes sence to use "using"
only if object has IDisposable interface, and second, that debug mode
slighty differs from Release mode in terms of memory management - i
shouldn't explcitly dereference objects in Release mode.

Don't confine that last statement to just Release mode. There's no real need
to dereference objects in almost all circumstances.
 
M

MuZZy

Jon said:
In release, or only in debug? I'd be very surprised if it made any
difference in release mode.

I haven't tested that in Release mode - just Debug.
Now having read all the responces i fnally got the whole picture i hope:)
 
O

Ollie Riches

Surely assigning an object to null at the end of a method will be optimized
out during compilation?

Or am I trying to second guess what goes on during compliation :)

Ollie


But there again how can I second guess what the compile or
 
J

Jon Skeet [C# MVP]

Ollie Riches said:
Surely assigning an object to null at the end of a method will be optimized
out during compilation?

Not during C#->IL compilation.
Or am I trying to second guess what goes on during compliation :)

Yup...
 
J

Jon Skeet [C# MVP]

Jon Skeet said:
Are you sure it's a case of how it's compiled? I was under the
impression that it was how it was being *run* - which would make sense,
as if you're not running in the debugger, you don't need to know the
value of c after the last use even if the debug symbols are present.

When I get the chance I may test this in each configuration just to see
what happens...

I've just tested this, and Nick's absolutely right. In fact, unlike
some other optimisations, this one looks like it's at the IL level,
rather than just being dependent on whether or not there's a pdb file
present. Here's the test program I used:

using System;

class Test
{
~Test()
{
Console.WriteLine ("Finalized");
}

static void Main()
{
Test t = new Test();

GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine ("After first collect");
t = null;
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine ("After second collect");
}
}

When compiled in non-debug mode (or in debug "pdb only" mode) the
results are:

After first collect
Finalized
After second collect

When compiled in full debug mode, the results are:
Finalized
After first collect
After second collect
 
J

Jon Skeet [C# MVP]

Jon Skeet said:
When compiled in non-debug mode (or in debug "pdb only" mode) the
results are:

After first collect
Finalized
After second collect

When compiled in full debug mode, the results are:
Finalized
After first collect
After second collect

Those should be the other way round, of course. Doh! Don't you just
hate it when you spot something wrong at the moment that the articles
just been posted?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top