4
votes

I'd like to have a rigorous understanding of the contract of GCHandle.Alloc(Object).

I understand from the documentation that if I call:

GCHandle gc = GCHandle.Alloc(foo);

foo will be guaranteed not to be garbage collected until I call:

gc.Free();

I also understand that foo will get collected if the AppDomain is unloaded.

What I would like to check is if a call to Alloc without a call to Free is effectively the same (in the eyes of the GC) as a root reference.

To be clear if this is true then the scope of the GCHandle variable gc has no impact on the lifetime of foo. If Free is not called foo lives until the AppDomain unloads.

E.g. An object calls Alloc on itself and does not retain the GCHandle instance, it will live until the AppDomain is unloaded.

Is this correct?

Some references:

http://msdn.microsoft.com/en-us/library/a95009h1.aspx

http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.gchandle.free.aspx

http://blogs.msdn.com/b/clyon/archive/2005/03/18/398795.aspx

1
Why do I get the feeling that somebody will try to take advantage of this behavior?Jim Mischel
Many already have. It's called the singleton pattern, and you don't need to use GCHandle to do it. Simply create a static reference to an object. It won't be collected unless the reference is cleared.Brent M. Spell
@Jim I'm honestly just asking to clarify my understanding - I'm not planning an app domain long life cycle (I am looking at safe life cycles for delegates wrapped up as callbacks for unmanaged code and thought this clarification would be useful).morechilli

1 Answers

1
votes

Correct. The GCHandle variable has no effect on the lifetime of the object that you pass to GCHandle.Alloc(). I verified this with the following two classes:

class Test
{
   public Test ()
   {
      System.Runtime.InteropServices.GCHandle.Alloc(this);
   }
   ~Test ()
   {
   }
}
class Server : MarshalByRefObject
{
   public Server ()
   {
      new Test();
   }
   public void Collect ()
   {
      GC.Collect();
      GC.WaitForPendingFinalizers();
   }
}

And the following test code:

AppDomain ad = AppDomain.CreateDomain("test");
Server svr = (Server)ad.CreateInstanceAndUnwrap(
   System.Reflection.Assembly.GetExecutingAssembly().FullName, 
   typeof(Server).FullName);
for (Int32 i = 0; i < 100; i++)
   svr.Collect();

Using this example, if you set a breakpoint in the Test class's finalizer, you will note that it is not called during any of the GC.Collect/WaitForPendingFinalizers calls. The breakpoint is hit, though, when the appdomain is unloaded.