A survey of garbage collection and the changes CLR 4.0 brings in Part 3 - series of what is new in CLR 4.0
接前篇Continue the previous posts
.net垃圾回收和CLR 4.0对垃圾回收所做的改进之一
弱引用Weak Reference
弱引用是相对强引用来说的。强引用指的是根有一个指针指向对象。弱引用是通过对强引用加以弱化而得到的。这个弱化的手段就是用System.WeakReference类。所以精确地说,强引用指的是根有一个非WeakReference类型的指针指向对象,而弱引用就是根有一个WeakReference类型的指针指向对象。垃圾收集器看到一个WeakReference类型的根指向某个对象,就会特别处理。所以在垃圾收集器创建对象引用关系图的时候,如果遇到一个弱引用指针,那么垃圾收集器就不会将其加入图中。如果一个对象只有弱引用指向它,那么垃圾收集器可以收集此对象。一旦将一个强引用加到对象上,不管对象有没有弱引用,对象都不可回收。
Weak reference is corresponding to strong reference. Strong reference is that a root is pointing to an object. Weaken a strong reference and get a weak reference. The facility to weaken strong reference is the System.WeakReference class. An exact definition is that, strong reference is that a root has a non-System.WeakReference type pointer pointing to an object, and weak reference is that a root has a System.WeakReference type pointer pointing to an object. Garbage collector treats the roots of WeakReference type in a special way. Therefore, when garbage collector is building the reference relationship graph, if there is a weak reference pointer, and then garbage collector will not append it to the graph. If an object only has weak references pointing to it, garbage collector can collect the object. Once a strong reference is pointing to the object, no matter whether the object has weak references or not, the object can not be collected.
垃圾收集器对WeakReference类的特别处理从new操作就开始。通常的类,只要new操作,就会从托管堆分配空间,而WeakReference类的new操作不是这样做的。我们先来看WeakReference类的构造函数:
The special treatment that garbage collector deals with WeakReference class starts from the new operation. A common class will allocate space from managed heap when new operation, but CLR does not do like that for WeakReference class when new operation. Let’s look at the constructors of WeakReference class:
WeakReference(Object target);
WeakReference(Object target, Boolean trackResurrection);
此二构造函数都需要一个对象的引用,第二个构造函数还需要一个布尔值参数来表示我们是否需要跟踪对象的重生。此参数的意义后文会交代。
The two constructors both need a reference of an object, and the second constructor needs one more, a parameter of boolean type to indicate whether we need to track the resurrection of the object. The exact meaning of the parameter will be talked about later.
假设我们有两个类MyClass和MyAnotherClass,都有Finalize方法。我们声明两个对象:
Suppose we have two classesMyClass and MyAnotherClass, and both of classes have Finalize method.
MyClass myObject = new MyClass();
MyAnotherClass myAnotherObject = new MyAnotherClass();
当我们用这样的代码声明一个弱引用对象: WeakReference myShortWeakReferenceObject = new WeakReference( myObject );
When we use such a snippet of code to declare a weak reference object: WeakReference myShortWeakReferenceObject = new WeakReference( myObject );
垃圾收集器内部有一个短弱引用表,用这样声明的弱引用对象将不会在托管堆中分配空间,而是在短弱引用表中分配一个槽。此槽中记录对myObject的引用。New操作将此槽的地址返回给myShortWeakReferenceObject变量。
Garbage collector has a short weak reference table inside, the weak reference object declared in such a way will not allocate space from managed heap, and a slot will be allocated in the short weak reference table for the weak reference object. The slot records the referene of myObject. New operation returns the address of the slot to myShortWeakReferenceObject variable.
如果我们用这样的代码声明一个弱引用对象(我们要跟踪该对象的重生): WeakReference myLongWeakReferenceObject = new WeakReference( myAnotherObject, true );
When we use such a snippet of code to declare a weak reference object(We need to track resurrection of the object): WeakReference myLongWeakReferenceObject = new WeakReference( myAnotherObject, true );
垃圾收集器内部有一个长弱引用表,用这样声明的弱引用对象将不会在托管堆中分配空间,而是在长弱引用表中分配一个槽。此槽中记录对myAnotherObject的引用。New操作将此槽的地址返回给myLongWeakReferenceObject变量。
Garbage collector has a long weak reference table inside, the weak reference object declared in such a way will not allocate space from managed heap, and a slot will be allocated in the long weak reference table for the weak reference object. The slot records the referene of myAnotherObject. New operation returns the address of the slot to myLongWeakReferenceObject variable.
垃圾收集器此时的收集流程是这样的: The collecting process of garbage collector in this scenario is looking like the follwoing:
1. 垃圾收集器建立对象引用图,来找到所有的可到达对象。前文已经说过如何建立图。特别的地方是,如果遇到非WeakReference指针,就加入图,如果遇到WeakReference指针,就不加入图。这样图就建好了。Garbage collector builds up the reference ralationship graph to find all the reachable objects. The way to build up the graph is mentioned earlier. The special space is, if encounters a non-WeakReference pointer, then appends it to the graph, if meets a WeakReference pointer, then does not append it to the graph. The graph is built in this way.
2. 垃圾收集器扫描短弱引用表。如果一个指针指向一个不在图中的对象,那么此对象就是一个不可到达的对象,垃圾收集器就将短弱引用表相应的槽置空。Garbage collector scans the short weak reference table. If a pointer pointing to an object not in the graph, then the object is not a reachable object, and then garbage collector sets the slot to null in the short weak reference table.
3. 垃圾收集器扫描结束队列。如果队列中一个指针指向一个不在图中的对象,此指针将被从结束队列移到待结束队列,同时此对象被加入引用关系图中,因为此时此对象是Finalize可到达的。Garbage collector scans the Finalization queue. If a pointer in the queue pointing to an ojbect not in the graph, then the pointer will be moved to the ToBeFinalized queue, and the object will be appended to the reference relationship graph, because the object is Finalize reachable at this time.
- 4. 垃圾收集器扫描长弱引用表。如果一个指针指向一个不在图中的对象(注意此时图中已包含Finalize可到达的对象),那么此对象就是一个不可到达的对象,垃圾收集器就将长弱引用表相应的槽置空。Garbage collector scans the long weak reference table. If a pointer pointing to an object not in the graph, then the object is not a reachable object, and then garbage collector sets the slot to null in the long weak reference table.
- 5. 垃圾收集器夯实(压缩)托管堆。 Garbage collector tamps(or compacts) the managed heap.
短弱引用不跟踪重生。即垃圾收集器发现一个对象为不可到达就立即将短弱引用表相应的槽置空。如果该对象有Finalize方法,并且Finalize方法还没有执行,所以该对象就还存在。如果应用程序访问弱引用对象的Target属性,即使该对象还存在,也会得到null。
Short weak reference does not track resurrection. It means that garbage collector sets the slot to null in the short weak reference table as soon as finds an object not reachable. If the object has a Finalize method, and its Finalize method has not been executed, then the object still exists. If the applicaiton accesses the Target property of the weak referene object, and gets null even though the object still exists.
长弱引用跟踪重生。即垃圾收集器发现一个对象是Finalize可到达的对象,就不将相应的槽置空。因为Finalize方法还没有执行,所以该对象就还存在。如果应用程序访问弱引用对象的Target属性,可以得到该对象;但是如果Finalize方法已经被执行,就表明该对象没有重生。
Long weak reference tracks resurrection. It means that garbage collector does not set the slot to null in the long weak reference table when garbage collector meets an object is Finalize reachable. Because the Finalize method has not been executed, the object still exists. If the application accesses the Target property of the weak reference object, and gets the object. But if the Finalize method has been executed, then the object is not resurrected.
按照上面的例子,如果执行如下代码会发生什么呢?Continue the above example, what will happen if we execute the following code?
//File: MyClass.cs using System; using System.Collections.Generic; using System.Text; namespace ConsoleApplication2 { class MyClass { ~MyClass() { Console.WriteLine("In MyClass destructor+++++++++++++++++++++++++++"); } } }
//File: MyAnotherClass.cs using System; using System.Collections.Generic; using System.Text; namespace ConsoleApplication2 { public class MyAnotherClass { ~MyAnotherClass() { Console.WriteLine("In MyAnotherClass destructor___________________________________"); } } }
//File: Program.cs using System; using System.Collections.Generic; using System.Text; namespace ConsoleApplication2 { class Program { static void Main(string[] args) { MyClass myClass = new MyClass(); MyAnotherClass myAnotherClass = new MyAnotherClass(); WeakReference myShortWeakReferenceObject = new WeakReference(myClass); WeakReference myLongWeakReferenceObject = new WeakReference(myAnotherClass, true); Console.WriteLine("Release managed resources by setting locals to null."); myClass = null; myAnotherClass = null; Console.WriteLine("Check whether the objects are still alive."); CheckStatus(myShortWeakReferenceObject, "myClass ", "myShortWeakReferenceObject"); CheckStatus(myLongWeakReferenceObject, "myAnotherClass", "myLongWeakReferenceObject"); Console.WriteLine("Programmatically cause GC."); GC.Collect(); Console.WriteLine("Wait for GC runs the finalization methods."); GC.WaitForPendingFinalizers(); //Check whether the objects are still alive. CheckStatus(myShortWeakReferenceObject, "myClass ", "myShortWeakReferenceObject"); CheckStatus(myLongWeakReferenceObject, "myAnotherClass", "myLongWeakReferenceObject"); Console.WriteLine("Programmatically cause GC again. Let's see what will happen this time."); GC.Collect(); //Check whether the objects are still alive. CheckStatus(myShortWeakReferenceObject, "myClass ", "myShortWeakReferenceObject"); CheckStatus(myLongWeakReferenceObject, "myAnotherClass", "myLongWeakReferenceObject"); myAnotherClass = (MyAnotherClass)myLongWeakReferenceObject.Target; Console.ReadLine(); } static void CheckStatus(WeakReference weakObject, string strLocalVariableName, string strWeakObjectName) { Console.WriteLine(strLocalVariableName + (weakObject.IsAlive ? " is still alive." : " is not alive.")); Console.WriteLine(strWeakObjectName + (weakObject.Target != null ? ".Target is not null." : ".Target is null.")); Console.WriteLine(); } } }
请大家想一想如果MyAnotherClass类没有Finalize方法呢? Please think about what if the MyAnotherClass class does not have Finalize method.
或者:如果我们注释掉这行: GC.WaitForPendingFinalizers();, 试着多执行此代码多次, 看看每次会输出什么呢?是不是Finzalization方法被执行的时机不确定? Or, if we comment the line: GC.WaitForPendingFinalizers();, and try to execute the code multiple times, let’s see what will be the outputs. Is it that the time to execute Finalization methods is uncertain?
弱引用是为大对象准备的。在实际当中,如果不用弱引用,只用强引用,则用过了该大对象,然后将强引用置null,让GC可以回收它,但是没过多久我们又需要这个大对象了,但是已经没有办法找回原来的对象,只好重新创建实例,这样就浪费了创建实例所需的计算资源;而如果不置null,就会占用很多内存资源。对于这种情况,我们可以创建一个这个大对象的弱引用,这样在内存不够时将强引用置null,让GC可以回收,而在没有被GC回收前,如果我们短时间内还需要该大对象,我们还可以再次找回该对象,不用再重新创建实例。是不是节省了一些开销?
Weak reference is for large objects. In our daily practice, if we do not use weak reference, only use strong reference, it happens that we use a largae object and set it to null and let GC collect it, and we need the large object soon later, but there is no way find the original object back, we have to instanciate a new large object. That way we are wasting computer resources to create the old instance, but if we do not set the object to null, it occupies lot of memory. In this case, we can create a weak reference of the large object, and set strong reference object to null when there is not enough memory and let GC collect the large object, and we can find the large object back when we have requirement before the large object has not been collected by GC. No need to create a new instance, does this way save costs?
垃圾收集的一般流程The common process of garbage collection
以下是垃圾收集的一般流程,受应用场景(如服务器应用,并发和非并发)影响,具体的垃圾回收流程可能有所不同。The following is the common process of garbage collection, because of different scenarios and factors(like server application scenario, concurrent or non-concurrent), the actual processes are a little different.
1. 挂起.net应用的所有线程Suspend all the threads of .net application
2. 找到可回收的对象Find all the reclaimable objects
3. 回收可回收的对象并压缩托管堆Reclaim the reclaimable objects and compact the managed heap
4. 继续.net应用的所有线程Resume all the threads of .net application
垃圾收集的模式The modes of Garbage collection
CLR4.0之前,一共有三种模式的GC,分别针对不同类型的应用程序而优化:Server版非并发GC, Workstation版并发GC, 和Workstation版非并发GC. Before CLR 4.0, there are three different modes of GC, which are optimized for different types of applications: Server version Non–concurrent GC, Workstation version concurrent GC, and Workstation version Non-concurrent GC.
Server GC - Non Concurrent
Server GC是针对服务器应用而进行优化的,目的是为了保证其高吞吐量和高的规模可扩展性。The server non-concurrent GC is optimized for high throughput and high scalability in server applications.
Server GC 为每一个处理器分配一个托管堆和一个GC线程。各GC线程独立工作在各自的堆上,这样最大程度地减少了锁,从而保证了此种情况下的高效。这些GC线程是非并发的(第0代,第1代,第2代都是非并发的,即在收集时都要停止所有应用程序的线程,直到收集结束)。同时这些GC线程是专门的线程,不同于应用程序的线程。The server GC allocates one heap and one GC thread for each of processors. The GC threads work on their respective heaps, and this minimizes the possibilities to lock resources, and ensures high efficiency in the server situation. The GC threads are non-concurrent(Gen 0, Gen 1 and Gen 2 are non concurrent, meaning that garbage collector suspends all the threads of application at collecting cycles of generations until the collecting finishes). Meanwhile the GC threads are dedicated threads, different from the threads of application.
实际的Server GC工作流程是这样的:The actual work flow of server GC is looking like the following:
1. 应用程序的线程在其托管堆上分配空间The application threads are doing allocations on their corresponding managed heap;
2. 其托管堆没有更多空间来分配托管对象They run out of allocations on the heaps their allocating on;
3. 于是触发一个事件,让GC线程来做收集,并等待GC线程完成收集That signals an event to wake the GC threads to do a GC and waits for it to finish;
4. GC线程运行,完成收集并发出完成事件(在GC线程运行期间,应用程序线程是暂停的)GC threads run, finish with the GC and signal an event that says GC is complete (When GC is in progress, all the application threads are suspended);
5. 应用程序的线程继续运行The application threads resume running.
这种类型的GC只有在多处理器的机器上可见,如果你在单处理器上的设置这种模式,那你将得到workstation版本非并发的GC。The Server Non-concurrent GC is only available on multi processor machines. If you try to set the server GC on a machine with single processor you will instead get the workstation version with non concurrent GC.
Asp.net的应用在多cpu的机器上默认使用这种模式,还有其他一些类型的服务器应用也在多cpu的机器上使用这种模式。例如如果你想在Windows服务上使用server GC模式,你可以在应用程序配置文件中做如下设置:ASP.NET applications use the mode by default on multiproc machines, as well as a number of other types of server applications. For example, if you want to use the server GC in a windows service you can do so by setting in the application’s config file:
<configuration>
<runtime>
<gcServer enabled="true" />
</runtime>
</configuration>
Workstation GC – Concurrent
Winform应用程序和Windows services 服务程序默认采用这种模式。This is the default setting for win forms applications and windows services.
这种模式是针对交互式应用程序优化的,这种程序要求应用程序不能暂停,即使是一个相对很短暂的时间也是不行的。因为暂停进程会让用户界面闪烁或者当点击按钮的时候感觉应用程序没有响应。The Workstation GC is optimized for interactive applications that can’t allow for the application to be paused even for relatively short periods since pausing the threads would cause flicker in the user interface or make the application feel non responsive to button clicks etc.
在这种模式下,第0代和第1代的收集仍然是要暂停应用程序的线程直到收集完毕,因为第0代和第1代的收集速度很快,所以没有必要去做并行。所谓的并发是在需要第2代收集时,我们可以选择让第2代收集并行地执行或者暂停应用程序的所有线程直到收集结束。如果我们没有做选择,那么默认的就是第2代收集是并行的。In this mode, the collection of gen 0 and gen 1 still need to pause all the threads of application until the collection of gen 0 and gen 1 finishe. Since the gen 0 and gen 1 collections are very fast, it does not make sense to have concurrent GC when we do gen 0 and gen 1 collections. The concurrent is that we can choose whether we do concurrent collection when it is gen 2 collection or suspend all the threads of application until the collection finish. If we do not make the choice, then the default is that the gen 2 collection is concurrent.
有一个专门的GC线程来并行地收集垃圾。在第2代收集时,用更多CPU周期和堆内存来换取更短的用户界面停顿时间。There is a dedicated GC thread collecting garbage in parallel mode. This is done by trading a little bit more CPU cycles and heap memory usage for shorter UI pause time when doing gen 2 collections.
并行收集仍然需要在收集过程中暂停应用程序多次,但这几次暂停相对较短,对UI线程影响较小。应用程序线程在并行收集进行中可以同时分配对象。这就要求第0代有较大的空间和较高的触发Gen 0收集的阀值。如果应用程序用完第0代的内存,而并行收集还没有进行完毕,那么应用程序不得不暂停下来,等待并行收集的完成。Concurrent collection still needs to suspend the application multiple times during the concurrent collecting cycle. , but the the times of pause are shorter than usual, have less impact on the UI thread. The application can allocate objects while the concurrent collection is going on. This requires that the gen 0 has bigger space and higher threshold to trigger collection on gen 0. If the application exhausts the memory of gen 0, and the concurrent collection is not finished, then the application has to suspend to wait for the concurrent collection finishes.
如果我们要做选择,让应用程序在并行模式下运行,可以在应用程序的config文件中写上: If we make the choice to let the application run under concurrent mode, please leave this in the config file of the application.
<configuration>
<runtime>
<gcServer enabled=“true"/>
</runtime>
</configuration>
注: Jeffrey Richter似乎在其书<<CLR via c#>>中说concurrent GC只有在多CPU机器上有。但是经过Mark的调查,实际上concurrent GC在单CPU上也有。
Note: Jeffrey Richter seemed to mention in his book <<CLR via c#>> that concurrent GC is only available on multiple CPUs machine, but according to Mark’s investigation, concurrent GC is also available on single CPU machine.
Workstation GC – Non Concurrent
这种模式与Server GC类似,推荐为那种运行在单个cpu机器上服务类型的应用程序使用。与Server GC不同的是:其收集线程即等同于应用程序线程。这是这种模式的工作流程:The mode is similar to the Server GC mode, and is recommended for the server applications running on single processor machine. The collection happens on the same threads of application, which is different from what the Server GC does. The following is the work flow of the mode:
1) 应用程序线程在堆上分配对象Application threads are doing allocations on the heap;
2) 第0代没有足够空间It runs out space in gen 0.
3) 在同一线程上引发垃圾收集It triggers a GC which will be running on this very thread;
4) 垃圾收集器调用SuspendEE函数暂停应用程序线程GC calls SuspendEE to suspend the application’s threads;
5) GC做收集工作GC does its work;
6) GC调用RestartEE来继续应用程序的线程GC calls RestartEE to restart the application’s threads;
7) 应用程序的线程继续运行Application threads start running again.
可以修改应用程序的配置来把 concurrency 关闭。We could close concurrency using the following setting in the application’s config file.
<configuration>
<runtime>
<gcConcurrent enabled="false" />
</runtime>
</configuration>
下面是三种模式的对比表This is the comparison matrix of the modes:
Concurrent WS | Non-Concurrent WS |
Server GC |
|
Design Goal |
Balance throughput and responsiveness for client apps with UI |
Maximize throughput on single-proc machines |
Maximize throughput on MP machines for server apps that create multiple threads to handle the same types of requests |
Number of heaps |
1 | 1 | 1 per processor(HT aware) |
GC threads |
The thread which performs the allocation that triggers the GC |
The thread which performs the allocation that triggers the GC |
1 dedicated GC thread per processor |
Execution Engine Suspension |
EE is suspended much shorter but several times during a GC |
EE is suspended during a GC |
EE is suspended during a GC |
Config setting |
<gcConcurrent enabled="true"> |
<gcConcurrent enabled="false"> |
<gcServer enabled="true"> |
On a single processor machine |
WS GC + non-concurrent |
以上我们说到了CLR 4.0以前垃圾收集的大部分方面。 掌握前面的知识有助于理解CLR 4.0所带来的变化。We introduced most of aspects of garbage collection prior to CLR 4.0. Master the legacy to help undetstand the changes CLR 4.0 brings in.
CLR 2.0以后GC有哪些变化呢?What are the changes since CLR 2.0?
CLR V4.0带来的后台垃圾收集The background GC brought to you by CLR 4.0
前面我们已经提到已经有了并发垃圾收集。后台垃圾收集是来替代并发垃圾收集。后台垃圾收集是并发垃圾收集的进化。We mentioned there has been concurrent GC. Background GC is to replace concurrent GC. Background GC is an evolution to concurrent GC.
当应用程序的活动量不大(包括分配和修改引用)并且堆不太大时,并发垃圾收集工作得好。由并发垃圾收集带来的延迟是可接受的。但是当人们编写更大的应用程序,需要用到更大的堆,这时由并发收集带来的延迟就变得不可接受。When the program activity (including making allocations and modifying references) is not really high and the heap is not very large concurrent GC works well – the latency caused by the GC is reasonable. But as people start writing larger applications with larger heaps that handle more stressful situations, the latency can be unacceptable.
后台垃圾收集的显著改进是:在后台垃圾收集进行中,如果需要的话,我们可以做短的垃圾收集(第0代和第1代收集)。与并发垃圾收集类似,后台垃圾收集也是只有在全部垃圾收集时才有。并且短的垃圾收集(第0代和第1代收集)总是按阻断方式进行垃圾收集(暂停应用程序)。后台垃圾收集在一个专门的线程上运行。那些与后台垃圾收集同时进行的短垃圾收集(第0代和第1代收集)被称为前台垃圾收集。
The significance of background GC is we can do ephemeral GCs(gen 0 and gen 1 GC) while a background GC is in progress if needed. As with concurrent GC, background GC is also only applicable to full GCs and ephemeral GCs are always done as blocking GCs(suspend the application), and a background GC is also done on its dediated GC thread. The ephemeral GCs(gen 0 and gen 1 GC) done while a background GC is in progress are called foreground GCs.
于是,当后台垃圾收集正进行中而第0代空间不足,就会触发一个第0代垃圾收集(有可能升级成第1代收集,取决于垃圾收集器的内部决策)。后台垃圾收集线程会经常检查安全点(比如前台垃圾收集发生)。当有前台垃圾收集时,后台垃圾收集会自己暂停并让前台垃圾收集可以开始开始工作。在前台垃圾收集完成之后,后台垃圾时线程和应用程序的线程就继续它们的工作。
So when a background GC is in progress and you’ve allocated enough in gen0, we will trigger a gen0 GC (which may stay as a gen0 GC or get elevated as a gen1 GC depending on GC’s internal tuning). The background GC thread will check at frequent safe points (ie, when we can allow a foreground GC to happen) and see if there’s a request for a foreground GC. If so it will suspend itself and a foreground GC can happen. After this foreground GC is finished, the background GC thread and the user threads can resume their work.
后台垃圾收集不仅在较低的代将无用的对象清除,同时它提高了对象停留在短生命周期段的限制--如果后台垃圾收集进行中需要扩展堆,我们可以在第1代收集时这样做。而这在CLR1.0和CLR2.0只有在第2代收集时才会发生。
Not only does this allow us to get rid of dead objects in young generations, it also lifts the restriction of having to stay in the ephemeral segment – if we need to expand the heap while a background GC is going on, we can do so in a gen1 GC. In CLR1.0 and CLR 2.0, this only happens when gen 2 GC .
后台垃圾收集比并发收集的好处体现在:在后台收集进行中,应用程序耗尽了第0代的空间,如果是并发收集,应用程序必须暂停以等待并发收集完成。而现在不同了,可以用前台收集对第0代或者第1代进行收集。于是就不必等待。同时,如果需要,可以在第1代收集就扩展堆(增加内存段),而不必等到第2代收集。这减少了让UI应用程序暂停的机会。Advantages of Background GC(versa to concurrent GC) are : During background GC, the application runs out memory in gen 0, if it is concurrent GC, the application has to suspend and wait for the finish of concurrent GC. But now it is different, we can use forground GC to do collection on gen 0 or gen 1. therefore no need to wait for the finish of concurrent GC. And meanwhile, we can expand the heap(by adding new memory segment) at gen 1 if required, do not have to wait until gen 2 GC. These minimize the possibilities to suspend the UI application.
我们通过并行地做更多事情改进了后台垃圾收集的性能,所以需要暂停应用程序线程的时间更短。我们现在没有在CLRV4.0中为Server GC提供后台垃圾收集。那将是后续的工作。We also made some performance improvement in background GC which does better at doing more things concurrently so the time we need to suspend managed threads is also shorter. We are not offering background GC for server GC in V4.0. That is coming work.
.net 3.5 SP1中的全部收集通知机制Full GC notification in .net 3.5 SP1
全部收集通知机制是为担负大量请求的服务器应用准备的。因为存在一些情况,CLR的全部收集会带来较大的延迟,从而影响服务器应用的性能,导致用户请求超时。针对这种情况,全部收集通知机制应运而生。当一个全垃圾收集将要开始时,你会得到通知并可以采取相应动作,如将请求导向别的服务器。
.net 3.5 SP1的GC类增加了几个方法:
GC.RegisterForFullGCNotification
并在System命名空间下增加了一个枚举
这个GC.RegisterForFullGCNotification 用来注册当前应用将要接收全部垃圾收集的通知。当一个全部垃圾收集将要发生时,注册了的应用程序就可收到通知。同时,当一个全部垃圾收集完成时,注册了的程序也会收到通知。这是GC.RegisterForFullGCNotification 的申明方式:
[(.LinkDemand, Name = "FullTrust")] RegisterForFullGCNotification( maxGenerationThreshold, largeObjectHeapThreshold )
maxGenerationThreshold是1-99之间的数.指的是第2代中存活的对象个数达到这个阀值就要通知. largeObjectHeapThreshold也是1-99之间的数.指的是大对象堆中分配的对象个数达到这个阀值就要通知.
WaitForFullGCApproach用来获得垃圾收集器是否将要进行全部垃圾收集. 它的声明是这样的:
[(.LinkDemand, Name = "FullTrust")] WaitForFullGCApproach()
或者
[(.LinkDemand, Name = "FullTrust")] WaitForFullGCApproach( millisecondsTimeout )
第2个调用方式可以带一个等待时间, 单位是毫秒. 其返回的是枚举GCNotificationStatus, 如果是GCNotificationStatus.Succeeded, 那么我们就可以将请求导向其他服务器.
与WaitForFullGCApproach类似的, WaitForFullGCComplete用来获得垃圾收集器是否已经完成全部垃圾收集, 它的声明是这样的:
[(.LinkDemand, Name = "FullTrust")] WaitForFullGCComplete()
或者
[(.LinkDemand, Name = "FullTrust")] WaitForFullGCComplete( millisecondsTimeout )
第2个调用方式可以带一个等待时间, 单位是毫秒. 其返回的是枚举GCNotificationStatus, 如果是GCNotificationStatus.Succeeded, 那么我们就可以将此服务器重新声明可以接受外界的请求.
WaitForFullGCApproach和WaitForFullGCComplete设计时就是要一起使用的。如果只使用一个而不使用另一个就会导致不可知的结果。
GC.CancelFullGCNotification是用来将已经注册的垃圾收集通知取消. 与GC.RegisterForFullGCNotification 一起成对配合使用.
通常的使用方法是这样的:
1.首先在主线程调用GC.RegisterForFullGCNotification 来准备收获通知
2.启动一个值守线程, 在值守线程中调用WaitForFullGCApproach和WaitForFullGCComplete来获取当前GC状态, 如果是将要进行全部垃圾收集,就做相应处理,如将请求导向其他服务器。如果是全部垃圾收集完毕,则可做相应处理, 如将此服务器标记为可接受请求.值守线程中需要不断循环来轮循GC状态.
3.当主线程将要结束, 调用GC.CancelFullGCNotification并发消息通知值守线程, 值守线程退出循环,结束.
4.主线程退出
对于asp.net应用, 可以在Application_Start中调用GC.RegisterForFullGCNotification并启动一个值守线程. 同时可以在Application_End中通知值守线程结束.
全部垃圾收集通知机制的典型用途就是在多服务器的场景.用NLB(Network load balance)搭建的Web Farm或者硬件load balance的多服务器场景. 一个比较复杂的场景是每个服务器上还有web garden设置.
注意事项: 全部垃圾收集通知机制只有在非并发模式时才可用. WinForm和Console应用都是默认为并发模式. 例如msdn上的那个例子程序,就需要在程序的app.config中加上
<configuration><runtime>
<gcConcurrent enabled="false" />
</runtime>
</configuration>
全文完. 谢谢各位!
参考文献References
Garbage Collection: Automatic Memory Management in the Microsoft .NET Framework By Jeffrey Richter http://msdn.microsoft.com/en-us/magazine/bb985010.aspx
Garbage Collection Part 2: Automatic Memory Management in the Microsoft .NET Framework By Jeffrey Richter http://msdn.microsoft.com/en-us/magazine/bb985011.aspx
Garbage Collector Basics and Performance Hints By Rico Mariani at Microsoft http://msdn.microsoft.com/en-us/library/ms973837.aspx
Large Object Heap Uncovered By Maoni Stephens http://msdn.microsoft.com/en-us/magazine/cc534993.aspx
Garbage collection in msdn http://msdn.microsoft.com/en-us/library/0xy59wtx.aspx
How does the GC work and what are the sizes of the different generations? By Tess Ferrandez http://blogs.msdn.com/tess/archive/2008/04/17/how-does-the-gc-work-and-what-are-the-sizes-of-the-different-generations.aspx
So, what’s new in the CLR 4.0 GC? By Maoni Stephens http://blogs.msdn.com/maoni/archive/2008/11/19/so-what-s-new-in-the-clr-4-0-gc.aspx
Using GC Efficiently – Part 1 http://blogs.msdn.com/maoni/archive/2004/06/15/156626.aspx
Using GC Efficiently – Part 2 http://blogs.msdn.com/maoni/archive/2004/09/25/234273.aspx
Using GC Efficiently – Part 3 http://blogs.msdn.com/maoni/archive/2004/12/19/327149.aspx
Using GC Efficiently – Part 4 http://blogs.msdn.com/maoni/archive/2005/05/06/415296.aspx
The mystery of concurrent GC http://www.julmar.com/blog/mark/PermaLink,guid,3670d081-0276-48e6-b97d-1b644093b52e.aspx
http://blogs.msdn.com/clyon/archive/2004/09/10/228104.aspx