user9623401
user9623401

Reputation:

Is safe to not call GC.KeepAlive() in this scenario?

let's say we have the following code running in release build:

class SomeClass
{
  // This field is initialized somewhere in the constructor (not shown here).
  public SomeOtherClass Value;

  ...
}

class SomeOtherClass {
  ...
  public void Call() {
     ...
  }
}
...

static void Main()
{
  SomeClass obj = new SomeClass();
  SomeOtherMethod(obj.Value);
  ...
}

static void SomeOtherMethod(SomeOtherClass value) {
  ... // do sth else first
  value.Call();   // value is a reference that points to `obj.Value` object in heap
}

I saw code like this all the time and at that time I was not familiar with how GC works. But now I know that the garbage collect may collect obj just after the runtime has retrieved obj.Value i.e. before SomeOtherMethod() is even called. So there is a chance (although unlikely, but still possible) that when SomeOtherMethod execute and before manipulate obj.Value the argument, obj has been marked as unreachable (therefore its member Value is also marked as unreachable) and collected by GC and their memory is recliamed, so it is illegal for SomeOtherMethod to access Value.

So I think a solution is to add GC.KeepAlive(obj) or GC.KeepAlive(obj.Value) as:

void MyMethod()
{
  SomeClass obj = new SomeClass();
  SomeOtherMethod(obj.Value);
  ...
  GC.KeepAlive(obj);   //  or GC.KeepAlive(obj.Value);
}

But I don't see many people(senior developer) doing this, and the code always running correctly without throwing any exception. So is my understanding incorrect or CLR has a workaround on this?

Upvotes: 0

Views: 54

Answers (1)

Marc Gravell
Marc Gravell

Reputation: 1062770

When obj.Value has been read, the value of that (which could be a reference, or a primitive/struct) is copied (to where doesn't matter; note that in the case of a reference it is only the reference that is copied, not the object that it refers to). At that point, it doesn't matter whether obj is collected - the copy of the value is independent.

Note that if you were using ref-return properties or naked fields with SomeOtherMethod(ref obj.Value), then things are slightly different, but in that case: we still know that the GC can follow managed pointers (aka ref), and if necessary this would keep the object alive.

So no, you don't need GC.KeepAlive. The times you need GC.KeepAlive are very niche and exotic (and usually involve things like finalizers and indirect (non-reference) relationships between data); in most everyday scenarios: the GC does what you want without needing your input.

Upvotes: 5

Related Questions