Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why garbage collector takes objects in the wrong order?

I have an application with two classes, A and B. The class A has inside a reference to class B. The destructors of the classes do some cleanup of resources but they have to be called in the right order, first the destructor of A and then the destructor of B.

What is happening is that somehow the destructor of B is called first and then the destructor of A is crashing because is trying to execute methods from a disposed object.

Is this behavior of the GC correct? I expected the GC to detect that A has a reference to B and then call the A destructor first. Am I right?

Thanks mates!

PD: In case of doubt about destructor/finalizer/disposer etc that's what we have:

~A()
{
    this.Dispose();
}

~B()
{
    this.Dispose();
}    
like image 979
Ignacio Soler Garcia Avatar asked Nov 12 '10 10:11

Ignacio Soler Garcia


People also ask

What are the three possible conditions for a garbage collection?

Conditions for a garbage collectionThe system has low physical memory. The memory size is detected by either the low memory notification from the operating system or low memory as indicated by the host. The memory that's used by allocated objects on the managed heap surpasses an acceptable threshold.

What causes high garbage collection?

The root cause of high-frequency garbage collection is object churn—many objects being created and disposed of in short order. Nearly all garbage collection strategies are well suited for such a scenario; they do their job well and are fast. However, this still consumes resources.

How do garbage collectors work?

When an object is no longer used, the garbage collector reclaims the underlying memory and reuses it for future object allocation. This means there is no explicit deletion and no memory is given back to the operating system.

Can garbage collector claim unmanaged objects?

Although the garbage collector is able to track the lifetime of an object that encapsulates an unmanaged resource, it doesn't know how to release and clean up the unmanaged resource. If your types use unmanaged resources, you should do the following: Implement the dispose pattern.


1 Answers

As others have noted, your finalizers are wrong, wrong, wrong. You cannot simply call Dispose in a finalizer and expect good things to happen. Read up on the correct way to implement the disposable pattern.

Getting that right is the beginning, not the end, of the work you have to do. In addition to all the other correct answers here, I note that:

  • the finalizer can (and usually does) run on a different thread. Finalizing resources that have an affinity to a particular thread is dangerous, you can run into deadlocks if you are not careful, and so on.

  • finalization can "resurrect" a dead object by assigning a reference to it to a variable that is in a live object. Do not do that. It is incredibly confusing.

  • finalizers can run on objects that were partially constructed when a thread abort exception happened. You cannot assume that any invariant that is true of a fully-constructed object is true of an object being finalized.

Writing a finalizer correctly is extremely difficult for all these reasons. Avoid, avoid, avoid.

like image 96
Eric Lippert Avatar answered Sep 28 '22 07:09

Eric Lippert