垃圾回收系列(3):CLR与JVM垃圾回收器的比较

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

垃圾回收系列(3):CLR与JVM垃圾回收器的比较

发布者:TerryLee | 分类:.NET框架 | Java技术 | 计算机科学

本文为垃圾回收讲座的第三篇,在前面两篇(一、二)文章里介绍了手工管理内存带来的一些问题,以及一些经典的GC算法。本文我们主要关注微软的CLR与JVM垃圾回收器方面的比较。我们知道CLR和JVM都采用了分代式垃圾回收器,而分代式垃圾回收器则基于以下几点假设:

1. 对象越新,其生存期就越短

2. 对象越老,其生存期就越长

3. 对堆的一部分执行GC比对整个堆执行GC要快

CLR和JVM尽管都采用了分代式垃圾回收器,但是它们在很多处理方面都有些不同:分代机制,大对象堆,回收模式,回收算法,寻找存活对象效率等。

分代机制

在CLR中,对象按年龄可以分为三代:第0代、第1代、第2代,如下图所示:

在这三代之间,对象代的提升过程,大家可以参考《CLR via C#》,里面有比较详细的介绍。

JVM中对于对象的分代式新生代和旧生代:

回收模式

在CLR4.0之前,提供三种不同的垃圾回收模式:工作站并发GC、工作站非并发GC以及服务器GC,如下图所示:

工作站非并发GC模式,没有专门的GC线程,而是由工作线程负责回收,在回收过程中,需要暂时挂起应用程序,回收结束后应用程序继续运行,所以在回收过程中会有应用程序暂停现象:

工作站并发GC模式,为了解决在执行垃圾回收时引起的应用程序暂停问题,会有一个专门的GC线程负责垃圾回收,大多数时间垃圾回收都可以应用程序并发执行,但是仅仅是针对Full GC,而对于第0代、第1代对象,仍然会使用非并发模式执行,并发垃圾回收本质上牺牲了更多的CPU时间和内存来换取应用程序暂停时间的减小:

服务器GC模式运行在多CPU服务器上,如果在单CPU机器上配置了使用服务器GC,不会起任何作用,垃圾回收仍然会使用工作站非并发模式执行。服务器GC模式为每个CPU分配一个专用的垃圾回收线程和一个托管堆,并且该垃圾回收线程具有较高的优先级,在执行垃圾回收期间,应用程序工作线程会暂时挂起:

CLR 4.0中提供了后台垃圾回收机制,用于取代并发GC。

JVM(以Hotspot为例)中使用的垃圾回收更为复杂,针对新生代、旧生代在工作站和服务器上,分别使用不同的垃圾回收模式,如下图所示:

在Client端默认的方式为串行GC,而在服务端,对于新生代和旧生代默认的方式分别为:并行回收GC和并行GC:

下图体现了默认串行GC与并行GC之前的区别,并行GC会把堆分成多个区,分区进行标记和回收,但这两种方式都会引起应用程序的暂停:

下图体现了默认的标记缩并回收与并发GC,在并发GC中,标记的总共分为三个阶段,分别为:Initial Mark、Concurrent Marking和Remark,只有在Initial Mark和Remark阶段才会引起应用程序暂停,而在Concurrent Marking和清除阶段都是与应用程序并发执行,并不会引起暂停:

回收算法

在CLR中有专门的大对象堆(LOH),超过85000字节的对象将会分配在LOH上面,只有在进行第2代对象垃圾回收时才会同时对LOH进行回收,即一次Full GC。第0代、第1代、第2代对象所在的堆称之为小对象堆(SOH)。

在CLR中,对于小对象堆SOH,采用的回收算法为标记-缩并算法,由于移动大对象比较耗费时间,所以在LOH上,CLR采用了标记-清除算法,即只做对象的清除并不对大对象堆进行压缩。

在JVM中,对于新生代对象采用节点复制算法进行回收,这也是为什么我们在上面的图中,新生代对象堆分为S0和S1的原因,有时也称之为为From和To;对于旧生代对象根据不同的回收模式,采用不同的回收算法:

串行GC:标记-缩并算法(滑动缩并)

并行GC:标记-缩并算法

并发GC:标记-清除算法

提高查找存活对象的效率

现在考虑这样一个问题,只对第0代(或者新生代)对象做回收,在查找存活对象过程中(不关是标记-缩并还是节点复制算法),从根集合开始,如果发现有一个根对象指向了第1代、第2代(或者旧生代),为了提高效率,垃圾回收器将会立即终止这条线上的查找:

这样带来的一个问题是:如果在第0代(或者新生代)分配的对象X,没有根对象指向它,但有一个第1代或者第2代(旧生代)的对象,指向了该新创建的对象:

此时虽然没有根对象可以到达新创建的对象X,但由于有其他代的对象指向它,所以它仍然不能当做垃圾回收,为了解决这个问题,人们提出了各种解决方案,常见的有:

1. 记忆集

2. 硬件标记

3. 虚拟页面标记

4. 字标记

5. 卡标记

由于硬件标记需要特定的硬件平台来支持,所以不具有可移植性,现在更多的垃圾回收器都使用了软件解决方案,在CLR和JVM上都使用了卡标记技术,即把托管堆分成一个一个的卡片,这个卡片代表一个抽象的概念(卡片的大小可以等于一个字,也可以等于一个虚拟页面)。如果有卡片上的对象发生了变化,则在Card Table中做一个标记,在查找存活对象时,除了要查找根对象之外,还有查找Card Table中标记的对象所引用的对象:

在CLR和JVM上所使用的卡片大小也不相同,CLR为128字节一个卡片,而JVM上为512字节一个卡片。在更新卡片过程中,它们都是按字节(Byte)进行更新而不是按位(Bit)进行更新。

总结

虽然CLR和JVM都采用了分代式垃圾回收器,但是它们在很多处理上都有区别,不管怎样,分代式垃圾回收器的一个原则就是把对象分成不同的区(按年龄也好、按对象大小也好),以便在不同的分区上采用不同的回收算法和模式。本系列前两篇文章链接:

相关文档
最新文档