I’m sure many of you have heard the term “object resurrection” with respect to the GC. It’s an interesting (but not very useful) way to illustrate object lifetimes and the role of finalization versus garbage collection. Basically, it’s a way to reference an object that has been finalized.
Here’s a rough description of how object resurrection can occur:
- A finalizable object is created. It reachable from user code and is considered “live”.
- Inside the object’s finalizer is a statement assigning the “this” pointer to a global object (like a static). Since it has a finalizer, a reference to the object is put on the finalization watchlist by the GC.
- At some point the object’s last strong reference is gone, the object is considered garbage. The object reference is then moved off the finalization watchlist and onto the freachable queue. Now the object is considered live again since it’s referred to by the freachable queue. and the object is considered “freachable”.
- When the finalizer thread runs the object’s finalizer, the object is removed from the freachable queue. The global object now points to the object, making it once again reachable from user code. The object is said to have been “resurrected” and is once again reachable from user code and considered “live”.
Here’s a code sample to illustrate:
public class ResurrectedObj
// this will resurrect the object by assigning
// it to the static reference
resurrectedReference = this;
public static ResurrectedObj resurrectedReference = null;
public static void
ResurrectedObj liveReference = new ResurrectedObj();
liveReference = null;
// liveReference is now dead and the object is put on the
// freachable queue
// at this point, the object previously referenced
// by liveReference is held alive by resurrectedReference
Unfortunately there are several implications to object resurrection which may not be immediately obvious. For these reasons we strongly recommend against resurrecting objects.
- Once the object has been resurrected, the GC has removed it from the finalization queue. This means when the object dies again, the finalizer will not be run a second time. To ensure the finalizer gets run, a call to GC.ReRegisterForFinalization() is required.
- Attempting to use an object after the finalizer has been run, can result in undefined behavior. Maybe the finalizer released an unmanaged resource, and you try to access it? This is the same reason why you are discouraged from accessing other managed objects from a finalizer: that object’s finalizer may have been run before yours.
- Any object referenced by your resurrected object will also be resurrected. If any of those objects are finalizable, their finalizers may have been run, and may be in an invalid state.
- As with all finalizable objects, multiple garbage collections are required to completely clean up the object, potentially hurting performance and memory usage.