|
Because those hundreds of gen0 collectiosn occur before the next gen2 collection
Christian Graus
Please read this if you don't understand the answer I've given you
"also I don't think "TranslateOneToTwoBillion OneHundredAndFortySevenMillion FourHundredAndEightyThreeThousand SixHundredAndFortySeven()" is a very good choice for a function name" - SpacixOne ( offering help to someone who really needed it ) ( spaces added for the benefit of people running at < 1280x1024 )
|
|
|
|
|
Thanks Christian,
1.
You mean in GC, if gen2 collection is decided to run, then gen2 collection has to wait for gen1 collection complete, and gen1 has to wait for gen0 collection complete?
2.
GC could only decide to run gen0 only, or gen1 + gen0, right?
regards,
George
|
|
|
|
|
George_George wrote: You mean in GC, if gen2 collection is decided to run, then gen2 collection has to wait for gen1 collection complete, and gen1 has to wait for gen0 collection complete?
Objects that are just allocated will be on gen0. Objects survived one cycle of GC will be in gen1 and all other will be in gen2. When GC collects memory, it initially collects gen0, if enough memory is not claimed it will go to gen1 and then to gen2.
All objects considered as garbage and don't have finalizers will be removed immediately. If finalizer is available, then it won't be removed immediately. GC will flag it and start calling it's finalize method in another thread. So it will be reclaimed in next cycle. This is why classes which have finalizers are performance costly.
George_George wrote: GC could only decide to run gen0 only, or gen1 + gen0, right?
I am not getting your point here. Can you explain it ?
|
|
|
|
|
Thanks N a v a n e e t h,
Your answer is already clear. I want to confirm that there is only gen0, gen1 and gen2, no other numbers?
regards,
George
|
|
|
|
|
|
Thanks for your clarification N a v a n e e t h,
regards,
George
|
|
|
|
|
To save time in garbage collection, .NET implements a 'generational' garbage collector. When a GC is complete, .NET remembers the first address it will give out to new objects. When the next GC occurs, to save time, it will only scan from that remembered address, unless doing so won't give up enough free memory. It's most likely that the objects that have been created most recently are no longer required, as many objects have very short lifetimes. Those that have been used less frequently are more likely to have very long lifetimes.
In fact .NET remembers not one address but two, creating three 'generations' of objects. The objects from the start of memory to the first address are 'generation 2', those between the two addresses are 'generation 1', and those after the second address are 'generation 0'. If an object survives a collection of its generation, it is promoted one generation. If generation 0 collection doesn't yield enough space, generation 1 is collected. Only if both don't yield enough space will generation 2 be collected.
The GC collects by marking every object that is still referenced by 'roots', such as thread stacks and static objects. It follows every reference from those objects and marks everything. Once everything that's referenced is marked, it then 'sweeps' the memory from first object to last and anything that isn't marked is cleaned up.
It's at this point that anything that isn't marked requires finalization - has a finalizer, and GC.SuppressFinalize hasn't been called - is added to the finalization queue. The GC then follows all the references from those objects and marks them to ensure that anything still needed by an object that requires finalization is kept, in case the finalizer needs to use those referenced objects.
Finally, once everything needing finalization is queued up, GC <i>compacts</i> the heap, moving every object (that isn't <i>pinned</i>) down in memory so it's next to the previous one with no gaps. It has to rewrite all the references to do this, but the result is hopefully a nice big empty block at the end of the heap.
There's a background thread, the finalizer thread, whose responsibility it is to process the finalization queue. Only this thread ever calls finalizer methods, and it does it asynchronously with respect to GC. The queue is considered a root so future collections can't collect this memory until the objects' finalizers have been run and they're no longer marked as needing finalization. So the objects that need finalization end up being promoted at least one generation, which means that they'll only be cleaned up when gen 0 collection doesn't give enough space or when the heuristics call for a collection of an older generation.
The trouble with the finalizer thread is that a small error in a finalizer can end up blocking it. That stops all finalizers running, and then the GC can't free anything that needed finalizing.
Best practice is to make very small wrapper classes for managing unmanaged resources, that pretty much just manage that resource. Don't allow any references to other objects. Implement the IDisposable interface and a finalizer, and a Dispose(bool disposing) method. In the Dispose method, clean up the unmanaged resource and call GC.SuppressFinalize(this) to stop the infrastructure having to queue the object for finalization.
In the rest of your code, ensure that you call Dispose on any object that implements it. Use the <code>using</code> block where possible, dispose member variables in the class's Dispose override, dispose of anything else once you're done with it. That means, if the class has implemented the dispose pattern properly, the object should never end up in the finalizer. It's a backstop, there if you forget, but in my view, a running finalizer is a bug.
DoEvents: Generating unexpected recursion since 1991
|
|
|
|
|
Good reply Mike, but I have some thoughts on the following
Mike Dimmick wrote: has a finalizer, and GC.SuppressFinalize hasn't been called - is added to the finalization queue.
I guess GC will put objects into finalization queue even SuppressFinalize() is called. This blog[^] has some good points. Look at the 1.1.2 Dispose Pattern section. Any thoughts ?
|
|
|
|
|
Every object that has a Finalize method will be placed on the finalization queue, there is no way to prevent this, and it's not the GC that does that.
What the SuppressFinalize method does is to remove the object from that queue, so that the GC will not place the object in the freachable queue when it's up for collection.
The finalization queue is somewhat misleadingly named, as it's not really a queue at all. It's just a collection of references.
Despite everything, the person most likely to be fooling you next is yourself.
|
|
|
|
|
Thanks Guffa,
You mean when SuppressFinalize is called, the object is still put on the finalizer queue, but when finalizer thread runs and checked SuppressFinalize is called already, it will simply remove the object from the queue and reclaim its memory, right?
regards,
George
|
|
|
|
|
I think he meant in other way. Initially all the objects require finalization will be placed into a finalization queue. SuppressFinalize() remove them from the queue, I guess.
|
|
|
|
|
Thanks for clarifying this, I do not know this point before.
regards,
George
|
|
|
|
|
George_George wrote: You mean when SuppressFinalize is called, the object is still put on the finalizer queue, but when finalizer thread runs and checked SuppressFinalize is called already, it will simply remove the object from the queue and reclaim its memory, right?
No, not quite. The effect is bascially the same, but that's not how it works.
I think that you are still confusing the finalizer queue with the freachable queue. The finaliser thread is not finalising object in the finalizer queue, it's finalising objects in the freachable queue.
All objects that have a Finalize method are placed in the finalizer queue when they are created. When the garbage collector finds an object to collect, it will check if it's in the finalizer queue. If it is, it will be placed in the freachable queue, and if it isn't, the memory can simply be reclaimed.
The SuppressFinalize method removes the object from the finalizer queue, so that it will not end up in the freachable queue.
Despite everything, the person most likely to be fooling you next is yourself.
|
|
|
|
|
Guffa wrote: The finaliser thread is not finalising object in the finalizer queue, it's finalising objects in the freachable queue.
Freachable queue is ReadyForFinalization queue, right ? So objects that have finalization method and not called SuppressFinalize() will be in freachable queue, right ? So objects that don't have finalizers will be in finalization queue ?
BTW, do you know any good tool which shows the heap allocations and memory details ? I tried .NET profiler, and it looks good. Any thoughts ?
|
|
|
|
|
N a v a n e e t h wrote: Freachable queue is ReadyForFinalization queue, right ?
Yes.
N a v a n e e t h wrote: So objects that have finalization method and not called SuppressFinalize() will be in freachable queue, right ?
Yes, when the garbage collector notices that they are up for collection, they will be moved there.
N a v a n e e t h wrote: So objects that don't have finalizers will be in finalization queue ?
No. Objects that do have a Finalizer method will be placed on the finalization queue.
Despite everything, the person most likely to be fooling you next is yourself.
|
|
|
|
|
|
Thanks Guffa,
Your reply is so comprehensive.
regards,
George
|
|
|
|
|
Guffa wrote: and it's not the GC that does that.
Thanks Guffa, but who else will be doing that then ?
|
|
|
|
|
The object is placed on the finalizer queue when it's created, not when it's collected, so it's the code that creates object that does that, not the garbage collector.
Despite everything, the person most likely to be fooling you next is yourself.
|
|
|
|
|
|
When I implement a finalizer, I ALWAYS use it to show that an object hasn't been disposed of - to my mind, this is it's only real use in our internal code.
|
|
|
|
|
Great reply.
This is not just a "useful answer", and it is not just a five, it is unquantifiable.
I would take off my hat if I had one...
|
|
|
|
|
Luc,
I'll join you, viva la revolution!
-Spacix
All your skynet questions[ ^] belong to solved
I dislike the black-and-white voting system on questions/answers.
|
|
|
|
|
|
I didn't get it either. Perhaps he's in his own little world.
I agree with your earlier post, that's a great response from Mike Dimmick. I was so impressed I went back and voted!
BDF
A learned fool is more a fool than an ignorant fool.
-- Moliere
|
|
|
|
|