|
I got interested in this and investigated, and wrote a massive reply about what was happening in the IL, all commented and everything, then it didn't post properly and I lost it.
Basically, it compiles it to a private that inherits from IEnumerator inside your class. It moves your code into the MoveNext routine of the private class, and splits it up with a case statement so each time MoveNext is called, it does the next bit of your routine up to the next yield return statement, and then sets the Current property of the private to be your yield return value. This means when you stick your class in a foreach loop, the foreach loop calls GetEnumerator, which returns the private class, and then calls MoveNext and keeps looking at the Current property.
Just write a simple class that implements IEnumerable, and a GetEnumerator() method, compile it, and take a look with reflector, you'll see what I'm babbling on about.
Simon
|
|
|
|
|
Cool, Simon. I have studied the code into the reflector, since I am a beginner for the reflector, I have a basic question, the code below means,
there is an internal variable whose name is <>1__state and type is int?
there is an internal variable whose name is <counter>5__1 and type is int?
private int <>1__state;
public int <counter>5__1;
regards,
George
|
|
|
|
|
Yes, that's right. That's what I was talking about. The variables named with < and > signs in them. They're named like that to ensure no clashes can occur with your variables because those characters aren't allowed to be used in variable names in c#
Simon
|
|
|
|
|
Great Simon!
I have also made some study from sample expansion of code from C# specification, at top of this page,
http://msdn.microsoft.com/en-us/vcsharp/aa336809.aspx[^]
especially 10.14.6 implementation examples.
My current confusion is, I think the yield return grammar is of too flexibility, any ideas in what situation should we use it (in the implementation samples, it is used in for loop, while loop and foreach loop, too flexibility to grasp)?
regards,
George
|
|
|
|
|
Has anyone had any experience with this technology?
I'm trying get some examples in C#.NET 2.0 but my attempts have been futile. The SDK provided by the developer only seems to let you download examples if you have .NET 1.0 installed.
Does anyone know of any good C#.NET 2.0 examples floating around?
modified on Thursday, May 1, 2008 9:38 AM
|
|
|
|
|
Hello everyone,
In the section "Finalization and Performance" of MSDN document,
http://msdn.microsoft.com/en-us/library/ms973837.aspx
What means "Older objects needing finalization might have to wait for dozens if not hundreds of gen0 collections before their space is reclaimed."? I am especially confused about what means "wait for dozens" and "if not...", seems there are some dependencies between gen2 collection and gen0 collection?
thanks in advance,
George
|
|
|
|
|
No, what happens is, there are three generations of objects. Briefly
Objects get created. They exist in gen0.
When gen0 is collected ( quickly ), they move to gen1 if they are still being held.
After time, if they still exist, they go to gen2.
For efficiency, gen2 items are collected far less often than gen0, So, if something stayed around for a while, it will take longer to be collected.
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,
I understand your points, but what means "if not hundreds of gen0 collections before their space is reclaimed"?
regards,
George
|
|
|
|
|
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
|
|
|
|
|