what’s new in the .NET CLR 4.0/4.5 GC (.NET 4/4.5里新的垃圾收集机制)

what’s new in the CLR 4.0/4.5 GC?

Background and Foreground GC in .NET 4

从CLR 1.0开始, 垃圾收集器Garbage Collector分为2种

1.工作站模式GC,又叫并发GC  [ Concurrent GC ],即为桌面程序准备的垃圾收集方式。

2.服务器模式GC,又叫非并发GC [Non-Concurrent GC ],即为服务器程序准备的垃圾收集方式。

  并发GC在一个专门的线程中运行。在执行垃圾收集时,其他托管线程会继续运行,其他托管线程在垃圾收集过程中会被挂起2次,而不是在垃圾收集的全部时间内都被挂起。托管线程在没有被挂起的时间内,可以继续在内存(专有名词:临时内存段Ephemeral  segments )中创建对象,这里的内存是指程序运行之初(确切的说是GC运行之前)就预留出来的Ephemeral  segments内存段。当在这些预留内存段内给不同对象分配内存直到这些预留内存被用完后,那些托管线程才会被阻塞。知道并发GC运行完成,托管线程才继续恢复运行,GC完成后,并发GC线程自己则挂起。这就是说,只要那些预留的Ephemeral  segments内存没有被耗尽,则GC垃圾收集就不会导致延迟。

  非并发GC的主要不同就是:它挂起其他所有托管线程,不允许在GC垃圾收集过程中执行内存分配。并且在服务器GC上,每个CPU都有一个专门的GC垃圾收集线程。几核CPU就有几个GC线程。在这个模式下,每个CPU也会创建一个堆Heap,即每一个cpu内核都有一个独立的堆Heap和一个GC线程。

  这两种垃圾收集方式的差异主要是执行垃圾收集的响应时间区别。

  非并发GC在执行垃圾收集时会阻塞其他线程,其他线程会被挂起。非并发GC并不是很关心实时响应时间。虽然是服务器端的GC,响应时间对服务器来说固然很重要,但实时响应时间不是主要考虑因素,它更关心的是更高的吞吐量和可靠性。

  非并发GC的时间更短,并发GC会导致垃圾收集过程变慢。

  如果在桌面程序上使用非并发GC,即服务器模式GC,则会导致桌面图形程序有界面闪烁,响应延迟等现象。这种现象就好比是Android程序和iPhone程序 对使用者的体验来说,因为android的Java程序始终是有一个垃圾收集的过程,UI线程可能存在被阻塞的情况,界面就不如iPhone那么流畅顺滑了。

  并发GC的主要缺陷是:在CLR1.0时代,在程序中拥有GB字节大小的托管堆Heap还很少,这时候在GC垃圾收集时挂起其他托管线程的等待时间还是可以忍受的。

  GC垃圾收集针对Gen 0,Gen  1,Gen  2,LOH的各个堆的收集方式,有分为:

    1. 临时垃圾收集ephemeral GC:仅发生在Gen 0 和Gen 1 的垃圾收集。

    2. 全完垃圾收集 Full GC:当发生Gen 2 以上的Gen垃圾收集 就成为 完全Full 垃圾收集。

 

  Gen 0,Gen 1 , Gen 2称为小对象堆(SOH),它们里面的对象都小于85000字节,另外一个则成为大对象堆,Large Object Heap(LOH)它里面的对象大于85000字节,在这个堆里也可能包含几个小于85000字节的对象,这些对象是CLR运行创建的对象。

  在托管堆中分配内存都是在一段连续的内存中,直接通过递增内存地址指针和清空相应的内存区域来分配内存,基本都是以连续块状来分拨内存。这个就非常的高效,这个与windows系统的内存分配方式有着很大的不同,Windows系统分配内存通过Windows堆管理器来执行任务,当有一个分配请求时,Windows堆管理器会使用内存中任意一个空闲的内存来满足要求,这可能导致内存不是连续的。当Windows系统运行很多个类似的程序,执行很多类似的请求后,内存碎片就会出现,而随着程序的增多,分配效率就会降低。这就是为什么在服务器端使用.NET 托管程序效率还比较可以的地方,优势在于快速的连续块状内存分配。

垃圾收集发生时,

  1. 在Gen 0 和Gen 1里是使用内存压缩compact方式,移除死亡对象,移动活动对象,压缩内存,使之出现连续的空闲内存块。在这2个堆中发的内存收集是临时垃圾收集ephemeral  GC 。CLR基于的假设是:认为大多数对象都是短暂活跃的。所以将他们放在Gen 0中。在Gen 0 中发生的垃圾收集也是非常高效的,大约在1ms以内。在Gen 0:Gen 1的垃圾收集比大约在10:1的时候,也就是10次Gen 0 收集时才发生1次Gen 1收集被认为是比较合理的,程序性能不会太差。在桌面程序中,即并发GC模式下,堆Gen 0 和Gen 1 的垃圾收集其实是一个阻塞其他托管进程的方式(类似非并发服务器模式)。ephemeral GC 也永远是一个阻塞过程。

  2. Gen 2稍微有点区别,CLR管理Gen 2的方式是分配更多的内存段,当Gen 2中对象回收时,CLR堆管理器会回收这些内存段,并在不需要某个内存段是彻底释放它,其调用系统的VirtualAlloc 和VirtualFree函数来分配和释放。但因为彻底分配和回收的代价比较高,CLR2.0里其实不会释放内存段到操作系统,而是就绪保留这段内存。所以在性能计数器中,观察Gen 2的大小会发现即使发生内存回收,但Gen 2 的指标曲线还是没有下降,而实际程序并没有使用那么多的内存。发生在这个代,如果Gen 2发生垃圾收集, Gen 0 和Gen 1 也都会发生垃圾收集,所以说发生在 这3代中的垃圾收集成为完全垃圾收集。

  3. LOH堆,这个堆其实可以看作是Gen 2的扩展,对这个LOH的垃圾收集也只会发生在Gen 2垃圾收集之后才会进行。LOH的垃圾收集也只会在完全垃圾收集中进行。这里面都是大对象。执行内存紧缩compact操作的代价与对象的大小成正比,对象越大,紧缩操作的开销就越高。所以在LOH中的操作不是紧缩compact,而是使用空闲列表来管理空闲内存。不会发生空闲内存合并(邻近空闲内存还是会合并),这个过程叫打扫Sweeping.

  在CLR2.0版本中,并发GC和非并发GC在mscorwks.dll中实现。

  在CLR4.0中,mscorwks.dll 变成了clr.dll,GC的实现模式在这个dll里面。

 

CLR 4.0 中引入了后台GC即 Background GC

  当一个程序的活动,例如为对象分配内存,或修改对象引用地址---都是操作托管堆的活动 不是很高或者托管堆不是很大的时候并发GC是很高效的。

  CLR4.0 中的Background GC  的一个主要区别是:它允许GC和内存分配操作同时进行,允许background  GC和Gen 0 和Gen 1 上的ephemeral  GC垃圾收集内存分配同时运行。这时候的ephemeral  GC又称为 Foreground GC 前台GC。

  说白了,background GC就是以前CLR2.0时期的Concurrent GC 并发GC。另外又引入了一个Foreground GC 前台GC。所以CLR 4.0 GC的重点是Foreground GC 前台GC。

  这里申明一个概念:垃圾收集一定发生在内存分配时。有内存分配需求时才去找空闲内存,不足才会发生垃圾收集。所以在在关闭程序时,不会发生垃圾收集。

这里再让我们来复习一下Concurrent GC:

 

上图表示当前Heap。

当需要执行一个完全垃圾收集收集 Full GC时,GC线程会扫描Gen 2,标记死亡对象;通过扫描栈(stack)、根对象引用列表(Root)、终结队列(Finalize)、句柄表(Handle)等来标记死亡对象。这时候,并发GC模式下,在保证有足够的内存情况下,托管线程依然能在Gen 0 和Gen 1中分配对象。如下图:

 

当预留内存用尽且还有内存分配请求时,EE执行引擎(Execution Engine)会被GC线程挂起,所有的托管线程就会挂起,这时候应用程序就会有延迟了。

 

如上图所示,这个时候程序有性能影响。一个GC线程无法同时处理上面2个操作,目前Ephemeral Segment的大小是16M,在16M用完之前,程序都可以一直运行,16M用完时,那新的分配任务就要等待。当手动调用GC.Collect()时遇到这种情况就会出现性能问题。

现在Foreground GC 前台GC就登场了。Background GC的工作方式与上面描述的一样。当B-GC 在执行一个Gen 2垃圾收集且Gen 0 和Gen 1 (即ephemeral segment) 需要扩展内存时,Foreground GC 就被触发。如下图:

 

这个ephemeral foreground thread,临时前台线程会标记死亡对象,并采用段交换(swap segments)的方式,而不是以前提到的将Gen 0 或Gen 1的对象提升到更高一级的Gen的做法将ephemeral segment空出来,而被交换出去的Gen 1 对象将变成新的Gen 2对象。

 

有了这个前台GC,应用程序就不需要等待Full GC完成就可以开始对象创建和分配了。

当前台GC运行的时候,后台GC和其他托管线程应该会阻塞,等前台GC完成了,它们才会继续运行。

在CLR4.0中,桌面程序Workstation APP ,已经可以使用这种方式了。服务器上使用background GC 和 foreground GC 是在CLR 4.5才实现。

 

 

 

转载于:https://www.cnblogs.com/softfair/archive/2013/04/24/what-is-new-in-the-NET-CLR-4-GC.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值