Reputation:
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
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