managed (/clr) destructors

  • Thread starter Thread starter Peter Oliphant
  • Start date Start date
P

Peter Oliphant

I would assume in the following that 'instance' being set to 'nullptr'
should cause the instance of myClass that was created to no longer have any
pointers pointing to it, and therefore be 'destroyed':

ref myClass
{
public:
myClass() {}
~myClass() {} // never called
!myClass() {} // never called
} ;

main()
{
myClass^ instance = gcnew myClass() ;
instance = nullptr ; // shouldn't this cause destruction?
}

However, as indicated, neither the destructor or finalizer get called when
'instance' is set to 'nullptr'. Is the object previously pointed to by
'instance' still around (.e., it still has memory resources)? Isn't having
all existing pointers to an object either being set to nullptr or going out
of scope cause the object to be destructed? If not, what am I missing here?
If so, why is neither the destructor or finalizer called?

For context, using VS C++.NET 2005 Express in /clr mode, I'm trying to count
(using a static class variable) the number of existing instances of myClass.
It is easy to count the creations (bump counter in each constructor), but
how do I count the destructions?

thanks in advance for responses! : )

[==P==]
 
Hi Jeff,

Thanks for the response! : )

Ok, got a few questions then. How do I "force garbage collection"? Don't
know exactly what 'stack semantics' are (in contrast to the code sample I
provided), could you provide an equivalent to my code that IS in stack
semantic form?

Are you saying that the resources for the object previously pointed to by
'instance' are still 'valid' (even though there is no way to get to it from
the code since no pointers to it exist anymore) until the 'garbage
collector' decides to start garbage collecting, at which point the resources
may be released if needed?

The big question is as follows: Is there any call to a class that indicates
that it has just become elligible for garbage collection (that is, no
pointers exist that point to it)? That is, something that would trigger in
the sample code I gave when the only (or last) pointer to the object is set
to nullptr?

[==P==]

JAL said:
Peter... When a managed class is no longer reachable from root it is
eligible
for garbage collection. You can force the garbage collector just as a test
case to force the call to Finalize. In your example you are not using
stack
based semantics so the destructor will not be called. I wrote an artice
here
that may be of help.

http://www.geocities.com/jeff_louie/deterministic_destructors.htm

Jeff

Peter Oliphant said:
I would assume in the following that 'instance' being set to 'nullptr'
should cause the instance of myClass that was created to no longer have
any
pointers pointing to it, and therefore be 'destroyed':

ref myClass
{
public:
myClass() {}
~myClass() {} // never called
!myClass() {} // never called
} ;

main()
{
myClass^ instance = gcnew myClass() ;
instance = nullptr ; // shouldn't this cause destruction?
}

However, as indicated, neither the destructor or finalizer get called
when
'instance' is set to 'nullptr'. Is the object previously pointed to by
'instance' still around (.e., it still has memory resources)? Isn't
having
all existing pointers to an object either being set to nullptr or going
out
of scope cause the object to be destructed? If not, what am I missing
here?
If so, why is neither the destructor or finalizer called?

For context, using VS C++.NET 2005 Express in /clr mode, I'm trying to
count
(using a static class variable) the number of existing instances of
myClass.
It is easy to count the creations (bump counter in each constructor), but
how do I count the destructions?

thanks in advance for responses! : )

[==P==]
 
Peter.... I have use GC.Collect to force garbage collection for testing only.
I have also thrown an exception in finalize to document that finalize is
being called as main is exited. As for stack based semantics it would look
like this:

RAII r; // thats it

When an object is unreachable it is not valid in the sense that its methods
can be invoked. There is no reference counting by the garbage collector, it
normally simply searches the tree from root when memory runs low and if an
object is not reachable from root the object it can be collected.
 
Aha, by george I think I've got it! :)

My main problem is that I got so use to 'old syntax' managed objects
requiring them to be instantiated by 'new'ing them into pointers. Didn't
realize the new /clr syntax allows for construction of non-pointer instances
('stack semantics'). Maybe the old syntax did to, but I kept getting a
compiler error if I tried to construct any other way (so I got use to doing
it this way always, except with things like 'int' and 'bool').That is, I
didn't realize that:

RAII r ;

was legal, I thought the only way to construct a managed object was like
this:

RAII^ r = gcnew RAII() ;

[ or, old syntax, RAII* r = new RAII() ; ]

One more question if you would be so kind. In order for the stack symantics
to be possible, doesn't it require the class have a default (i.e., no
parameter) constructor? Or, alternately, one would have to construct using a
valid constructor, ala (imagine the definition of call RAII is extended to
include a constructor of the form "RAII( int x)"):

RAII r(3) ;

Put simply, stack symantics still doesn't allow the creation of an object
instance without going though a valid public constructor, Yes?

[==P==]

JAL said:
Peter.... I have use GC.Collect to force garbage collection for testing
only.
I have also thrown an exception in finalize to document that finalize is
being called as main is exited. As for stack based semantics it would look
like this:

RAII r; // thats it

When an object is unreachable it is not valid in the sense that its
methods
can be invoked. There is no reference counting by the garbage collector,
it
normally simply searches the tree from root when memory runs low and if an
object is not reachable from root the object it can be collected.

Peter Oliphant said:
Hi Jeff,

Thanks for the response! : )

Ok, got a few questions then. How do I "force garbage collection"? Don't
know exactly what 'stack semantics' are (in contrast to the code sample I
provided), could you provide an equivalent to my code that IS in stack
semantic form?

Are you saying that the resources for the object previously pointed to by
'instance' are still 'valid' (even though there is no way to get to it
from
the code since no pointers to it exist anymore) until the 'garbage
collector' decides to start garbage collecting, at which point the
resources
may be released if needed?

The big question is as follows: Is there any call to a class that
indicates
that it has just become elligible for garbage collection (that is, no
pointers exist that point to it)? That is, something that would trigger
in
the sample code I gave when the only (or last) pointer to the object is
set
to nullptr?

[==P==]
 
Peter... Glad to hear that. As I recollect, if you don't write any
constructor the compiler will write a no arg constructor for you and only no
arg instantiation will be allowed. If you write any constructor, then only
your explicit constructors are available. There is no default copy
constructor or assignment operator for use on stack based ref classes. If you
try to return a stack based object from a method, the compiler will complain.
 
ref MyRefClass
{
public:
MyRefClass() {}
~MyRefClass() {}
} ;

main()
{
MyRefClass instance;
// destructor ~MyRefClass() is called here
}
 
bonk said:
ref MyRefClass
{
public:
MyRefClass() {}
~MyRefClass() {}
} ;

main()
{
MyRefClass instance;
// destructor ~MyRefClass() is called here
}

So, what the OP's example does? a memory leak?
main()
{
myClass^ instance = gcnew myClass() ;
instance = nullptr ; // shouldn't this cause destruction?
}

Regards,
--PA
 
I believe it doesn't create a memory leak, but instead frees up memory the
garbage collector may use when it decides to, but won't call the finalizer
or destructor UNTIL it decides to.

So it isn't a 'leak' in the sense that it's memory the system has 'lost' or
has allocated and won't free up, but is instead memory which the system is
still in control of and can be freed up when desired, and will be freed up
upon application closing. Leaks typical remain after the application has
been closed because the system thinks the memory is still being used and/or
has lost the ability to de-allocate it since no pointer to it exists.

In theory, no ref class can possibly leak, that's the whole purpose behind
garbage collection...

[==P==]

PS - I'm the OP, and my initials are PO....lol
 
Pavel... As written the storage on the heap will be released when the garbage
collector runs. However, this is not deterministic behaviour. You can still
have deterministic behaviour without stack based sematics if you are willing
to write code like:

RAII^ heaped= nullptr;
try
{
heaped= gcnew RAII();
heaped->SayHello();
Console::WriteLine(heaped->I);
}
finally
{
if (heaped != nullptr)
{
delete heaped;
heaped= nullptr;
}
}

But the stack based example is much simpler:

RAII r;
r.SayHello();
Console::WriteLine(r.I);

The destructor will be called even if an exception is thrown. So... this
smacks of a smart pointer, an exception safe object on the stack that
automagically releases resources newed on the heap.
 
Back
Top