Ok, they are destructors. Instead of getting called at destruction, they get called at some time in the future. Hopefully.
1st question steams from MSDN.
So perhaps the finalizer is managed as being accessed by a delegate. 1st GC pass sets an object's delegate to null and adds an entry for finalization. The object is not collected but their finalize methods are run. 2nd GC pass sees those objects as not having a finalizer to run and can therefore free their memory blob with no hassle.
Finalize Methods and Destructors
Implementing Finalize methods or destructors can have a negative impact on performance and you should avoid using them unnecessarily. Reclaiming the memory used by objects with Finalize methods requires at least two garbage collections. When the garbage collector performs a collection, it reclaims the memory for inaccessible objects without finalizers. At this time, it cannot collect the inaccessible objects that do have finalizers. Instead, it removes the entries for these objects from the finalization queue and places them in a list of objects marked as ready for finalization. Entries in this list point to the objects in the managed heap that are ready to have their finalization code called. The garbage collector calls the Finalize methods for the objects in this list and then removes the entries from the list. A future garbage collection will determine that the finalized objects are truly garbage because they are no longer pointed to by entries in the list of objects marked as ready for finalization. In this future garbage collection, the objects' memory is actually reclaimed.
Or perhaps we have sets with allocation IDs to ignore. I don't find this very important.
But why to require 2 passes? Cannot we just run the finalizer immediately before releasing a blob?
Perhaps this is consequence of having separated trace phases and a collection phases? Or maybe it's something dealing with multiple threads?
2nd question. To extend an object's lifetime, use GC.KeepAlive. I don't understand why this is needed.
[source lang="csharp"][font=courier new,courier,monospace]MyWin32.HandlerRoutine hr = new MyWin32.HandlerRoutine(Handler); MyWin32.SetConsoleCtrlHandler(hr, true);[/font][font=courier new,courier,monospace] // Give the user some time to raise a few events. Console.WriteLine("Waiting 30 seconds for console ctrl events...");[/font][font=courier new,courier,monospace] // The object hr is not referred to again. // The garbage collector can detect that the object has no // more managed references and might clean it up here while // the unmanaged SetConsoleCtrlHandler method is still using it. // Force a garbage collection to demonstrate how the hr // object will be handled. GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); Thread.Sleep(30000);[/font][font=courier new,courier,monospace] // Display a message to the console when the unmanaged method // has finished its work. Console.WriteLine("Finished!");[/font][font=courier new,courier,monospace] // Call GC.KeepAlive(hr) at this point to maintain a reference to hr. // This will prevent the garbage collector from collecting the // object during the execution of the SetConsoleCtrlHandler method. GC.KeepAlive(hr); [/font] [/source]
It would appear to me a better way to deal with this problem would be to have explicit notion of what SetConsoleCtrlHandler does. But I see this might not be possible. In my head, hr would not be collected anyway as there's a reference to it on the stack.
But C# GC is smarter than that... it appears it will look ahead in code to see if something is not referenced anymore and thus clean it... perhaps C# stack/start set building does not work as I expect.
Anyone knows how KeepAlive helps with the problem? I'm inclined to speculate it might actually be NOP just to prevent the flow analyzer from marking the reference unused.