Doubts about .NET Garbage Collector
I've read some docs about the .NET Garbage Collector but i still have some doubts (examples in C#):
1)Does GC.Collect() call a partial or a full collection? 2)Does a partial collection block the execution of the "victim" application? If yes.. the开发者_Python百科n i suppose this is a very "light" things to do since i'm running a game server that uses 2-3GB of memory and i "never" have execution stops (or i can't see them..). 3)I've read about GC roots but still can't understand how exactly they works. Suppose that this is the code (C#):
MyClass1:
[...]
public List<MyClass2> classList = new List<MyClass2>();
[...]
Main:
main()
{
MyClass1 a = new MyClass1();
MyClass2 b = new MyClass2();
a.classList.Add(b);
b = null;
DoSomeLongWork();
}
Will b ever be eligible to be garbage collected(before the DoSomeLongWork finishes)? The reference to b that classList contains, can it be considered a root? Or a root is only the first reference to the instance? (i mean, b is the root reference because the instantiation happens there).
There are a couple of overloads for GC.Collect. The one without any parameters does a full collect. But keep in mind that it is rarely a good idea to call GC explicitly.
Garbage collections occurs within a managed application. The GC may have to suspend all managed threads in that process in order to compact the heap. It doesn't affect other processes on the system if that is what you're asking. For most applications it is usually not an issue, but you may experience performance related problems due to frequent collects. There are a couple of relevant performance counters (% time in GC, etc.) that you can use to monitor how the GC is performing.
Think of a root as a valid reference. In your example the instance of MyClass2 will not be collected if there are still references to it. I.e. if the instance pointed to by a is still rooted so will your instance of MyClass2. Also, keep in mind that GC is much more aggressive in release mode builds than debug builds.
GC.Collect() does a full collection. That's why it's not a good idea to call it yourself, as it can prematurely promote objects up a generation
AFAIK, not really. The GC blocks for so little time as to be inconsequential.
A stack GC root is an object that is currently being referenced by a method on the stack. Note that the behaviour is different between debug and release builds; on debug builds all variables in a method are kept alive until the method returns (so you can see their value in the debug window). On release builds the CLR keeps track of where method variables are used and the GC can remove objects that are referenced by the currently executing method but aren't used in the section of the method that is still to execute.
So, in your example, both a
and b
are not referenced again after the DoSomeLongWork()
call, so in release builds, during that method execution they would both be eligible for collection. In debug builds they would hang around until the main()
method returns.
Garbage collection is automatic. You don't have to interfere unless you are dealing with unmanaged resources. Garbage gets collected whenever the object is going out of scope; and at specific intervals whenever garbage-collector deems necessary - for instance, OS requires memory. This means there is no guarantee on how soon that will be, but your application will not run out of memory before memory from those objects is reclaimed.
Will b ever be eligible to be garbage collected(before the DoSomeLongWork finishes)?
Yes, whenever garbage collector finds it necessary.
Checkout Garbage Collector Basics and Performance Hints
Yes, GC.Collect() does a full collect, but you can do a manual GC.Collect(0) to do the gen 0 only.
All collections block the threads, but a partial collect does so only very briefly.
No, the instance of MyClass2 is still reachable in the other list. Both
a
andb
are root references (during DoSomeLongWork) but sinceb
is null that doesn't matter. Note that the GC is very tied in with the concept of reference types.b
is only a local var referencing the anonymous object. The 'roots' for the GC are static fields, everything on the stack and even CPU registers. In a simpler way: everything your code still has access to.
Will b ever be eligible to be garbage collected (before the DoSomeLongWork finishes)?
Potentially yes, provided a
and b
are expelled from the set of global roots by the compiler (i.e. not kept on the stack) then they can be reclaimed by a collection during DoSomeLongWork
.
I have found cases where .NET reclaims happily but Mono leaks memory.
The reference to b that classList contains, can it be considered a root?
Whether or not b
will be turned into a global root depends entirely upon the compiler.
A toy compiler might push references from function arguments and returned from function calls onto the stack and unwind the stack at the end of each function. In this case,
b
will be pushed onto the stack and, therefore, will be a global root.Production quality compilers perform sophisticated register allocation and maintain only live references on the stack, either overwriting or nulling references on the stack as they die. In this case,
b
is dead during the call toDoSomeLongWork
so its stack entry will have been nulled or overwritten.
None of this can be inferred from the source code without details of what exactly the compiler will do. For example, my HLVM project is only a toy at this stage, using the former technique, but it will actually collect a
and b
in this case because the call to DoSomeLongWork
is a tail call.
Or a root is only the first reference to the instance?
The global roots are the references the GC begins with in order to traverse all of the reachable data in the heap. The global roots are typically global variables and threads' stacks but more sophisticated GC algorithms can introduce new kinds of global roots, e.g. remembered sets.
精彩评论