user9623401
user9623401

Reputation:

Different scenarios when writing a Finalize method that accesses other objects whose type defines a Finalize method

I'm reading a book (CLR via C#) which says:

CLR doesn’t make any guarantees as to the order in which Finalize methods are called. So, you should avoid writing a Finalize method that accesses other objects whose type defines a Finalize method; those other objects could have been finalized already. However, it is perfectly OK to access value type instances or reference type objects that do not define a Finalize method.

I kind of get the idea but I'm not 100% sure why it is dangerous, so I will picture some different scenarios to verify whether or not my understanding on Finalize is correct

Let's say we have the following code:

class ClassA
{
  public ClassB Item;
  public ClassA(ClassB item) { this.Item = item };
  ~ClassA {
     ...  // might contain some time-consuming operations
     item.Call();
  }
}

class ClassB {
  public void Call() {

  }
   ~ClassB {
     Console.Write("Finalize run");   // simple statment, error-free
  }
}
...

static void Main()
{
  ClassB b = new Class B();
  ClassA a= new ClassA(b);
  ...   // do sth else
  GC.Collect();   // first GC
  ...   // do sth else
  GC.Collect();   // second GC
  ...   // do sth else
}

We know that The CLR uses a special, high-priority dedicated thread(let's call it Fthread) to call Finalize methods, and let's assume only two manual GC occurs, no auto triggered GC occurs.

So after first GC occurs, a and b marked as unreachable

Case OK 1: Fthread calls ~ClassA, followed by ~ClassB, b exists on the heap before second GC occurs

Case OK 2: Fthread calls ~ClassB, followed by ~ClassA, b exists on the heap before second GC occurs

Case Not OK:

Fthread calls ~ClassB, followed by ~ClassA, but ~ClassA executes some time-confuming operations before executing Call() on its member b. Second GC occurs, b is collected by GC, ~ClassA finishes the time-consuming operations, start to execute Call() on b, but b no longer exists in heap.

Based on the Case Not OK example, although it is rare, we still need to avoid writing a Finalize method that accesses other objects whose type defines a Finalize method. Is my understanding correct? I certainly won't writing a Finalize method that accesses other objects whose type defines a Finalize method, I know what is "ok" is complex; All I want to do is make sure my understanding is correct so that I know my understanding on Finalize method is correct

Upvotes: 1

Views: 83

Answers (1)

Marc Gravell
Marc Gravell

Reputation: 1062865

These problems are rarely interesting with trivial examples, because the problems only show in non-trivial ones, but if you want a few trivial examples:

  • someone passes null in as item on the constructor; your item.Call() throws an exception on the finalizer thread, and your app explodes in a shower of sparks
  • so you add null checking in ClassA's constructor; someone uses RuntimeHelpers.GetUninitializedObject to create an instance of ClassA that is all zeros/nulls; your item.Call() throws an exception on the finalizer thread, and your app explodes in a shower of sparks
  • someone leaves your ClassB instance in a state that Call doesn't anticipate, perhaps because of an exception, or perhaps because it was disposed (or perhaps it was being accessed concurrently by two threads in ways that left it so horribly mangled that it would need dental records, or perhaps someone used reflection to poke at it); now your item.Call() throws an exception - sparks resume

But ultimately, there is a much more important point:

Because you're using finalizers wrong

What you're asking about is not interesting because that isn't the intended and correct use of finalizers. This is a really advanced topic that is needed almost never (I've been using .NET since the earliest releases, and I think I might have used one, maybe two real genuine production finalizers - talking to unmanaged C/C++ APIs via P/Invoke). When you're already doing something inadvisable, asking "exactly how much can I get away with?" is the wrong question.

Upvotes: 2

Related Questions