GC destructor calls

M

mupp

I am wondering if someone could please explain the behaviour observed
in the following example:

class Contained
{
~Contained ()
{
}
}

class Container
{
Contained m_oContained;

public Container ()
{
m_oContained = new Contained ();
}

~Container ()
{
m_oContained = null;
}
}

Now create an instance of Container then immediately set to null so
that the newly created object is due for garbage collection e.g.:

Container l_oTest = new Container();
l_oTest = null;

What I don't understand is this: the Contained destructor is being
called BEFORE the Container destructor. This doesn't make sense to me
as a reference to the Contained object ( namely m_oContained ) still
exists when the Contained destructor is called. Indeed, when the
Container destructor is later called, the watch window reports
m_oContained as still referencing a valid object?!

Thanks in advance
 
A

Andreas Håkansson

Mupp,

There are alot to memory management in .net. I assume, by the way you
are
expecting the destructors to work, that you are coming from a C++
background.
If so then here is where you forgett what you know about destructors in C++
since
they work completly different in .NET.

First of all lets have a look at what ~ClassName actually means to .net. If
you have
a class which looks like this

class Foo
{
~Foo ()
{
// ...
}
}

When you compile this, you will actually end up with

class Foo
{
protected override void Finalize ()
{
try
{
//. . .
}
finally
{
base.Finalize();
}
}

So it actually rolls out to a Finalize call. Now what about Finalizers? Well
the only
thing you really need to know about them are

* You don't know IF they will be called on your objects
* You don't know WHEN they will be called on your objects
* You don't know IN WHAT ORDER they will be called on your objects

Sounds great doesn't it? ;) There are a bounch of issues (by design) in the
GC which
is too "blame" for this. You could read
http://www.csharphelp.com/archives2/archive297.html
for more information on how GC works.

So why does your destructors get called in the order they do? Well first of
all it comes
down to the third point in the list - by concidense. What about the
reference you claim to
exsist between the objects? Well it does exist but since the only way you
can get to the
Contained object is through your Container object, and there is no referens
to the latter
object - your Contained object is deemed unreachable thus it is a candidate
for memory
management.

This has been a very short description what is going on and why. I recommend
you read
the above link (and perhaps a few more - search on google) to get an
understanding on
how the GC works.

HTH,

//Andreas
 
D

David Browne

mupp said:
I am wondering if someone could please explain the behaviour observed
in the following example:
....

What I don't understand is this: the Contained destructor is being
called BEFORE the Container destructor. This doesn't make sense to me
as a reference to the Contained object ( namely m_oContained ) still
exists when the Contained destructor is called. Indeed, when the
Container destructor is later called, the watch window reports
m_oContained as still referencing a valid object?!

Because a finalizer just isn't a destructor.

It's just a very different thing.

Both objects become elegible for collection at the same time, but the order
in which the finalizers run in indeterminate. While the finalizers are
running the objects still reference each other and they are still "valid".
After an object is finalized it still exists, and won't be destroyed until
the next GC, at the very earliest. It is even possible for a
later-finalized object to resurect the finalized object, and make it
reachable again.

David
 
M

mikeb

mupp said:
I am wondering if someone could please explain the behaviour observed
in the following example:

class Contained
{
~Contained ()
{
}
}

class Container
{
Contained m_oContained;

public Container ()
{
m_oContained = new Contained ();
}

~Container ()
{
m_oContained = null;
}
}

Now create an instance of Container then immediately set to null so
that the newly created object is due for garbage collection e.g.:

Container l_oTest = new Container();
l_oTest = null;

What I don't understand is this: the Contained destructor is being
called BEFORE the Container destructor. This doesn't make sense to me
as a reference to the Contained object ( namely m_oContained ) still
exists when the Contained destructor is called.

One thing to realize is that even though m_oContained refers to the
Contained object, that reference is not rooted - it's not accessible
anymore, since the object that holds the reference is not accessible
anymore.

Any object which does not have a rooted reference is eligible for
garbage collection and therefore finalization. You have no control over
the order in which the garbage collector collects objects.
 
S

Sherif ElMetainy

Hello

The object becomes elegible for garbage collection when it is not reachable
by application code, i.e. it is not referenced directly or indirectly by any
local variable (variables in the stacks or all threads) or static fields. So
when you set container to null it is not reachable by the application code,
and so is contained. So they both go out of scope at the same time. I will
give you another example

class A {
public B b;
~A() {}
}

class B {
public A a;
~B() {}
}

A a1 = new A();
B b1 = new B();

a1.b = b1;
b1.a = a1;

a1 = null;
b1 = null;

In the above I created a circular reference, A refers to B, and B refers to
A. But the GC will collect both although they both contain reference to each
other, but both are not reachable by application code.

Best regards
Sherif
 
M

mupp

I am wondering if someone could please explain the behaviour observed
in the following example:

class Contained
{
~Contained ()
{
}
}

class Container
{
Contained m_oContained;

public Container ()
{
m_oContained = new Contained ();
}

~Container ()
{
m_oContained = null;
}
}

Now create an instance of Container then immediately set to null so
that the newly created object is due for garbage collection e.g.:

Container l_oTest = new Container();
l_oTest = null;

What I don't understand is this: the Contained destructor is being
called BEFORE the Container destructor. This doesn't make sense to me
as a reference to the Contained object ( namely m_oContained ) still
exists when the Contained destructor is called. Indeed, when the
Container destructor is later called, the watch window reports
m_oContained as still referencing a valid object?!

Thanks in advance

Thanks all

I have read about .NET garbage collection, but not in enough detail it
seems. Must've missed the part about an object 'not being reachable
from application code'.

Thanks again

mupp
 
J

Jeff Louie

mupp... Interestingly, it appears that Visual C++ 2005 supports mixing
"destructors" of managed and native types with the same syntax so:

String^ ReadFirstLineFromString(String^ path) {
StreamReader reader(path);
return reader.ReadLine();
}

As I understand it, in the case of a managed type the compiler
automatically
maps the streamreader destructor to IDisposable::Dispose, obviating the
need for "using". If the type lives on the managed "stack", the
destructor is
invoked when the type goes out of scope.

Regards,
Jeff
 

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