@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?