Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does GC put objects in finalization queue?

As I understand, garbage collector in c# will put all objects of a class into finalization queue, as soon as I implement destructor of the class. When I was reading documentation for GC.Suppresfinalize, it mentions that object header already has a bit set for calling finalize.

I am wondering that why the implementers of GC had to put all objects in a queue, and delay the freeup of memory by 1-2 cycles. Could not they just look at the bit flag while releasing memory, then call finalize of the object and then release memory?

No doubt I am an idiot and I not able to understand the working of GC. I am posing this question just to improve my understanding or fill the missing gap in my knowledge

EDIT : If the bit flag is for suppressfinalize, GC implementers could have added another flag in object header for this purpose, no?

like image 721
paseena Avatar asked Apr 11 '11 23:04

paseena


5 Answers

So it can run in a different thread and thus keep from blocking the main GC thread.

You can learn a lot about the GC from this MSDN article.

like image 113
jason Avatar answered Nov 16 '22 13:11

jason


There is a great explanation here

What are the Finalizer Queue and Control+ThreadMethodEntry?

Essentially the reasoning is that it may not always be ideal for the GC to have to wait on finalizer code to execute, so queuing finalizers allows finalization to be deferred until a time when it's more convenient.

like image 43
kqnr Avatar answered Nov 16 '22 13:11

kqnr


It's desirable for garbage collection pauses to be as short as possible. To that end, running finalizers is usually deferred to a later time, when the frantic work of garbage collection is done. It is instead done in the background on a separate thread.

like image 28
rlibby Avatar answered Nov 16 '22 12:11

rlibby


@Jason: this is true for the f-reachable queue. But IMHO it does not explain why there is the finalization-queue itself.

My guess is that the finalization-queue is there to add another information that helps the GC to distinguish between all the possible states of an object life-cycle.

The finalization flag in the object's header says "the object needs to be finalized" or "the object does not need to be finalized" but it does not say if the finalization has already occurred.

But to be honest I don't grasp why it's needed in the current finalization process implementation.

Indeed, here is the naive workflow I imagine possible without the finalization-queue:

  • when creating the object, if it has a finalizer, the GC sets the finalization flag;
  • if later SupressFinalize is called then the flag is zeroed;
  • now let's jump to when the GC collects the object, which is not referenced from anywhere: if the finalization flag is set then the GC puts a reference to the object into the f-reachable queue and lets the finalization thread operates;
  • later the finalization thread dequeues the reference, resets the finalization flag and runs the finalizer;
  • if the object wants to be refinalized later it could ReRegisterForFinalize to set the finalization flag again;
  • later the GC collects the object again: if the finalization flag is not set it knows there is nothing to do and then frees the object memory;
  • if the finalization flag is set the GC enqueues again a reference to the object into the f-reachable queue and there we go again for another round;
  • at some point in time the object is happy, completes the finalization and is collected; or the app-domain or process is shutdown and memory is freed anyway.

So seems like in these scenarios there is no need for a finalization-queue, only the finalization flag is useful.

One possible reason would be that from a conceptual point of view there might be a rule like: "an object is collected if and only if it is not referenced from any root". So not having a finalization queue, and basing the decision to collect an object on the object state itself, checking the finalization flag, is not compatible with this rule.

But really I don't think the GC implementation is based on the dogmatic application of such theoretical rules but only on pragmatic choices; so it's obvious I'm missing some key scenarios where the GC needs the finalization queue to know what to do when collecting an object, but which ones?

like image 1
Pragmateek Avatar answered Nov 16 '22 12:11

Pragmateek


The garbage-collector does not identify and examine garbage, except perhaps when processing the Large Object Heap. Instead, its behavior is like a that of a bowling-alley pinsetter removing deadwood between throws: the pinsetter grabs all the pins that are still standing, lifts them off the surface of the lane, and then runs the sweeper bar across the lane without regard for how many pins are on that surface. Sweeping out memory wholesale is much faster than identifying individual objects to be deleted. If 1% of objects have finalizers (the real number's probably even less), then it would be necessary to examine 100 object headers to find each finalizable object. Having a separate list of objects which have finalizers makes it unnecessary for the GC to even look at any garbage objects that don't.

like image 1
supercat Avatar answered Nov 16 '22 14:11

supercat