C++ and Garbage Collector?

  • Thread starter Thread starter Boris
  • Start date Start date
B

Boris

I am having really tough time finding anywhere on the web
concrete explanation (if any) of how Garbage Colletor
decided for C++ managed objects when object is ready to be
released, and why? Refer to simple example below with
questions inside comments.


__gc public class MyGCClass
{
...
}


void main()
{
simple_unmanaged_function();
}

void simple_unmanaged_function()
{
int x = 0;
MyGCClass* obj = new MyGCClass();
obj->DoSomething();

// Can GC release the MyGCClass instance
// at this point, if it is not being used
// until the end of the function?
// Must GC postpone collecting the instance
// until obj reference to goes out of
// of scope? i.e. for function to return
// What if different optimizations are
// set in "project"->"Property"->
// "c/c++"->"Optimizations"?
//

x = 5;

// Is this (set to NULL) necessary to
// mark obj as not referencing and
// available for GC?
// Or going out of scope is sufficient?
// obj = NULL;
// Seems to work without setting obj to NULL,
// but I just want to know what is good
// practice and why...
}


Thank you,
-Boris
 
Hi,
MyGCClass* obj = new MyGCClass();
obj->DoSomething();

// Can GC release the MyGCClass instance
// at this point, if it is not being used
// until the end of the function?

No it cannot as long as "obj" references the MyGCClass instance created. At
least it would be true in managed code, I am not so sure how
managed said:
// Seems to work without setting obj to NULL,
// but I just want to know what is good
// practice and why...


It is not necessary to reset "obj" to NULL - going out of scope is enough
for GC to recycle the instance being referenced as long as it is not
referenced elsewhere. Again it would be true for managed code, and unmanaged
C++ code can impose some change to that.
 
Dmitriy Lapshin said:
No it cannot as long as "obj" references the MyGCClass instance created.

Um, yes it can. If the CLR can determine that the obj variable will
never be read again, and it's not running under a debugger, the GC can
release the object. Here's some C# code to demonstrate that:

using System;
using System.Threading;

class Test
{
static void Main()
{
Test t = new Test();
t.DoSomething();

GC.Collect();
Console.WriteLine ("GC called");
GC.WaitForPendingFinalizers();
// Just to show it's not a race condition to the console
Thread.Sleep(2000);
Console.WriteLine ("Finished");
}

void DoSomething()
{
Console.WriteLine ("Do something called");
}

~Test()
{
Console.WriteLine ("Finalizer called");
}
}

On my box that produces output of:

Do something called
Finalizer called
GC called
Finished

(My guess is that the finalizer is being called slightly before the "GC
Called" line gets a chance to be printed - it's not *actually*
happening before the GC.Collect is called.)

This shows an object clearly being at least finalized (and, I believe,
garbage collected - that could be checked by creating a large object
and showing the memory taken before and after) before the variable
really goes out of scope.
 
Here is an example of "premature" release of instance of
managed class. Examine, "MyGCString::ToString(...)"
routine, and read the comments in there.


__gc public class MyGCString
{
private:
char* m_Value;
public:

static System::String* ToString(const char* str)
{
MyGCString *my_gc_str = new MyGCString(str);
const char *tmpValue = my_gc_str->Value;
System::GC::Collect();
// my_gc_str is collected at this point, tmpValue
is corrupted
// 1. my_gc_str has not gone out of scope, yet
// 2. my_gc_str has not been set to NULL
String *result = new String(tmpValue);
return result;
}

MyGCString(const char* str)
{
m_Value = NULL;
Clear();
Value = str;
}


~MyGCString()
{
Clear();
}

void Clear()
{
if(m_Value != NULL)
{
//Console::WriteLine("\n\nDestroying
m_Value = {0}", new String(m_Value));
free(m_Value);
m_Value = NULL;
}
}

void set_Value(const char* str)
{
if(str != NULL)
{
Clear();
m_Value = (char*) malloc(sizeof(char)*
(strlen(str)+1));
sprintf(m_Value, "%s", str);
}
}

const char* get_Value()
{
return m_Value;
}
}

-----Original Message-----
Dmitriy Lapshin [C# / .NET MVP] <x-code@no-spam- please.hotpop.com>
wrote:

No it cannot as long as "obj" references the MyGCClass
instance created.

Um, yes it can. If the CLR can determine that the obj variable will
never be read again, and it's not running under a debugger, the GC can
release the object. Here's some C# code to demonstrate that:

using System;
using System.Threading;

class Test
{
static void Main()
{
Test t = new Test();
t.DoSomething();

GC.Collect();
Console.WriteLine ("GC called");
GC.WaitForPendingFinalizers();
// Just to show it's not a race condition to the console
Thread.Sleep(2000);
Console.WriteLine ("Finished");
}

void DoSomething()
{
Console.WriteLine ("Do something called");
}

~Test()
{
Console.WriteLine ("Finalizer called");
}
}

On my box that produces output of:

Do something called
Finalizer called
GC called
Finished

(My guess is that the finalizer is being called slightly before the "GC
Called" line gets a chance to be printed - it's not *actually*
happening before the GC.Collect is called.)

This shows an object clearly being at least finalized (and, I believe,
garbage collected - that could be checked by creating a large object
and showing the memory taken before and after) before the variable
really goes out of scope.

--
Jon Skeet - <[email protected]>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
.
 
Boris said:
Here is an example of "premature" release of instance of
managed class. Examine, "MyGCString::ToString(...)"
routine, and read the comments in there.

I'm afraid at this stage it's beyond my knowledge - I don't know much
at all about MC++.
 
This is probably general question about GC:

my_gc_obj.DoSomething();

If DoSomething() doesn't access/modify any of the
my_gc_obj member variables, can GC release my_gc_obj
before DoSomething() returns?

Or even better, if optimization is so smart, can GC
release my_gc_obj before DoSomething() is even called?

In other words, what is the earliest time that GC decides
a specific object may be released? Is it if the object is
not being referenced any longer. But what does it really
mean these days (see example above)?

-Boris
 
Boris said:
This is probably general question about GC:

my_gc_obj.DoSomething();

If DoSomething() doesn't access/modify any of the
my_gc_obj member variables, can GC release my_gc_obj
before DoSomething() returns?

Yes, oddly enough - at least in C#.

Here's an example in C# which demonstrates it:

using System;
using System.Threading;

public class Test
{
public void DoSomething()
{
Console.WriteLine ("Doing something");
GC.Collect();
GC.WaitForPendingFinalizers();
Thread.Sleep(1000);
Console.WriteLine ("Done something");
}

public static void Main()
{
new Test().DoSomething();
}

~Test()
{
Console.WriteLine ("Finalizer running");
}
}

It shouldn't be too hard to write a similar MC++ app to test whether it
occurs there too.
Or even better, if optimization is so smart, can GC
release my_gc_obj before DoSomething() is even called?

Not sure about that. I wouldn't have *thought* so, but I wouldn't like
to say for sure.
In other words, what is the earliest time that GC decides
a specific object may be released? Is it if the object is
not being referenced any longer. But what does it really
mean these days (see example above)?

Deep question, unfortunately. This can cause problems, too - I can't
get to Chris Brumme's Blog at the moment, but there's an example there.
IIRC, it goes something like:

o Suppose you have a class X with a handle to an unmanaged resource
o You have a method X.y which is something like:

UnmanagedHandle localHandleVariable = this.handle;
// Dodgy point between lines
DoSomethingWith(localHandleVariable);

The GC could be invoked when execution was between those two lines of
code. If there are no other references to the object, its finalizer
could be run - which would no doubt release the handle.

If you want to prevent the GC from collecting "this" (or anything else)
until the end of the method, you can use:

GC.KeepAlive(this); // Or whatever

Hope this helps a bit. The documentation for GC.KeepAlive gives a
fuller example.
 
Back
Top