第十五节:垃圾回收模式

CLR启动时,会选择一种GC模式。在进程的生存期内,这个模式不能改变。有以下两种基本的GC模式。

1.工作站    这个模式为客户端应用程序优化垃圾回收器。垃圾回收器假定机器上运行的其它应用程序对CPU资源的要求不高。工作站模式有两个子模式:有并发回收器的工作站,以及无并发回收器的工作站。

2.服务器    这个模式为服务器端的应用程序优化垃圾回收。垃圾回收器假定机器上没有运行其它应用程序,并假定机器上所有CPU都是用来执行垃圾回收的。该GC模式造成托管堆被分成几个区域,每个CPU一个区域。开始一个垃圾回收时,垃圾回收器会在每个CPU上运行一个线程:每个线程都和其他线程并发回收它自己的区域。对于那些工作者线程的行为一致的服务器应用程序,并发回收可以很好的进行。这个功能要求应用程序在多个CPU计算机上运行,使线程能真正的进行工作,从而获得性能提升。

应用程序默认以“工作站”GC模式运行,并启用并发回收器。但是,寄宿了CLR的服务器应用程序(比如ASP.NET或Sql Server)可请求CLR加载服务器GC。如果服务器应用程序在单处理器上运行,CLR会加载没有并发回收器的“工作站”的GC模式。

自宿主CLR应用程序 可创建一个配置文件告诉CLR使用服务器回收器。在这个配置文件中,要为应用程序添加一个gcServer元素。下面是一个示例配置文件:

<configuration>

  <runtime>

    <gcServer enabled="true"></gcServer>

  </runtime>

</configuration>

应用程序运行时,可查询GCSettings类的只读Boolean属性IsServerGC来询问CLR它是否在“服务器”GC模式下运行:

Console.WriteLine("Application is runing with server GC="+GCSettings.IsServerGC);

“工作站”GC模式可以使用并发或者非并发方式运行。在并发方式中,垃圾回收器有一个额外的后台线程,它能在应用程序运行时并发回收对象。一个线程因为分配一个对象造成第0代超出预算时,垃圾回收器会先挂起所有线程,再判断要回收哪些代。如果垃圾回收器需要回收的是第0代或第1代,那么会和平常一样继续。但是,如果要回收第2代,就会增大第0代的大小,以便在第0代分配新对象。然后,应用程序的线程恢复运行。

应用程序线程运行时,垃圾回收器有一个普通优先级的后台线程标记出不可达的对象,该线程和应用程序的线程竞争CPU时间,这会导致应用程序的任务执行的慢一些。一旦标记好对象,垃圾回收器就会再次挂起所有线程,如果垃圾回收期决定压缩内存,内存将被压缩,根引用会被修改,应用程序的线程将恢复运行,这一次垃圾回收器所花费的事件比平常少,因为不可达的对象集合已经构造好了。但是,垃圾回收器也可能决定不压缩内存,实际上,垃圾回收期更喜欢这种方式。如果你有大量内存,垃圾回收期便不会压缩;这会提高应用程序的性能,但会增大应用程序的工作集。使用并发垃圾回收器时,一般会发现应用程序使用的内存超过了使用非并发垃圾回收器时消耗的内存。

总之,并发回收会给用户带来更好的交互体验,所以最适合交互式CUI和GUI应用程序。但是,对于某些应用程序,并发回收实际是有害的,会导致使用更多的内存。测试自己的应用程序时,应该使用并发和非并发两种方式进行测试,查看哪种方式能使用应程序获得最好的性能和最好的内存占有率。

为了告诉CLR不要使用并发回收。可创建一个包含gcConcurrent元素的应用程序配置文件。如下所示:

<runtime>

    <gcConcurrent enabled="true"></gcConcurrent>

 </runtime>

除了刚才描述的模式,垃圾回收器还支持可自由同步的分配。在多个处理器系统中,第0代托管堆分区成多个内存区域,每个线程一个,这就允许多个线程同时分配,不要求对堆独占的访问。

虽然GC模式是针对进程配置的,而且在进程运行期间不能修改,但你的应用程序可以使用GCSettings类的GCLatencyMode属性对垃圾回收进行某种程度的控制。这个读写属性能设为GCLatencyMode枚举类型中的任何值,如下所示:

Batch(服务器GC模式的默认值)  在工作站GC模式中,这个延迟模式关闭并发GC,

在服务器GC模式中,这是唯一有效的延迟模式。

Interactive(工作站GC模式的默认值)  在工作站GC模式中,这个延迟模式会打开并发GC。在服务器GC中,这个延迟模式是无效的。

LowLatency  在工作站GC模式中,在短期的,时间敏感的操作中(比如动画绘制)使用这个延迟模式;在这种情况下,对第2代回收可能造成混乱。在服务器GC中,这个延迟模式是无效的。

对于LowLatency  模式,有必要多讲几句。一般情况下,要用这个模式来执行一次短期的、时间敏感的操作,再将模式设回普通的Batch或Interactive。在模式设为LowLatency  期间,垃圾回收器会全力避免执行任何第2代回收,因为他会花费较长时间。当然,如果调用GC.Collect(),第2代仍会被回收。此外,如果Windows告诉CLR系统内存低,垃圾回收器也会执行第二次回收。

在LowLatency  模式中,你的应用程序抛出OutOfMemoryException的机率会大一些。所以,你尽可能短时间的处在这个模式中,避免分配太多对象,避免分配大对象,并用一个约束执行区域将模式设回Batch或Interactive。另外需要注意的是,延迟模式是一项进程级的设置,而可能有多个线程并发运行。在一个线程使用该设置期间,其他线程可能试图更改这个设置。所以,假设有多个线程都要操作这个设置,可考虑更新某个计数器(更新计数器需要通过Interlocked的方法进行)。一下代码展示了正确的使用LowLatency 

private static void LowLatencyDemo()

        {

            GCLatencyMode oldMode = GCSettings.LatencyMode;

            System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions();

            try

            {

                GCSettings.LatencyMode = GCLatencyMode.LowLatency;

            }

            finally

            {

                GCSettings.LatencyMode = oldMode;

            }

        }

原文地址:https://www.cnblogs.com/bingbinggui/p/4467722.html