how th开发者_如何学编程e mark and sweep phases of .NET GC can run concurrently with application threads ?
I'm not sure it does. According to Maoni Stephens blog.
During a concurrent GC we need to suspend managed threads twice to do some phases of the GC.
She doesn't spell out those phases.
Also from the entry
Concurrent GC, on the other hand, runs concurrently with the managed threads to the following extend:
§ It allows you to allocate while a concurrent GC is in progress.
However you can only allocate so much – for small objects you can allocate at most up to end of the ephemeral segment. Remember if we don’t do an ephemeral GC, the total space occupied by ephemeral generations can be as big as a full segment allows so as soon as you reached the end of the segment you will need to wait for the concurrent GC to finish so managed threads that need to make small object allocations are suspended.
§ It still needs to stop managed threads a couple of times during a concurrent GC.
During a concurrent GC we need to suspend managed threads twice to do some phases of the GC. These phases could possibly take a while to finish. We only do concurrent GCs for full GCs. A full GC can be either a concurrent GC or a blocking GC. Ephemeral GCs (ie, gen0 or gen1 GCs) are always blocking. Concurrent GC is only available for workstation GC. In server GC we always do blocking GCs for any GCs.
You might also be interested in this video.
This is just an educated guess, the exact details of the collector are not available anywhere. The Shared Source version of the CLR doesn't have the goodies.
As blogged, a concurrent collection only happens during the gen #2 collection, the one that can bite because there is no reasonable upper limit on the number of objects that might be in that generation. And can visibly affect the responsiveness of a GUI app on a workstation. Gen #0 and #1 are already collected, leaving a decent chunk of memory to allocate. It can have started the graph of gen #2 objects that it found while collecting 0 and 1.
I think the GC first suspends all threads so it can safely walk their stack and CPU registers to find object references. That can happen pretty quickly. And adds them to the graph.
It can now release the threads, they can keep running without fear of object references getting invalid because nothing is being moved yet. Allocations from gen #0 are no problem, maybe it can even collect #0 if it fills up. The blog post suggests it does.
Meanwhile, the collector can walk gen #2 and add references to the graph from the objects there. And find out what objects are no longer referenced. Threads are running while this happens. Unless the lower generations fill up, then a thread that allocates runs into a lock.
Next it needs to suspend the threads again so it can compact gen #2, moving objects and updating their references. The amount of time that could take is pretty unpredictable. I have no insight if it will move a gigabyte of objects to fill up a hole of 16 bytes that became free early in the heap. I seriously doubt it, that takes an easy 200 msec.
One consequence of this theory is that it won't release gen #2 objects that became unreferenced while the threads were running during the collection. That might a way to test this theory, although I don't really see a good way to do so.
精彩评论