深入理解Java虚拟机笔记—垃圾收集器

如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。Java虚拟机规范中对象垃圾收集器应该如何实现并没有任何规定,因此不同的厂商,不同版本的虚拟机所提供的收集器可能会有很的差别,并且一般会提供参数供用户根据自己的应用特点和要求组合出各个年代所使用的收集器。下面是Sun HotSpot虚拟机1.6版本Update22包含的所有收集器:

file

上图中,如果两个收集器之间存在连线,就说明它们可以搭配使用。

1.Serial收集器

Serial收集器是最基本、历史最悠久的收集器,曾经(在JDK1.3.1之前)是虚拟机新生代的唯一选择。这是一个单线程的收集器,但它的“单线程”的意义并不仅仅说明它只会使用一个CPU或一条收集线程去完成垃圾工作,更重要的是它进行垃圾回收都,必须暂停其它所有工作线程(Sun将这件事情称之为“Stop The World”),直到它收集结束。下面是Serial/Serial Old收集器的运行过程:

file

到目前为止,Serial收集中是虚拟机运行在Client模式下的默认重新代收集器。它简单而高效(与其它收集器的单线程相比),对于限定单个CPU环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。在桌面应用场景中,分配给虚拟机的内存一般来说不会太大,收集几直兆甚至一两百兆的新生代,停顿时间完全可以控制在几十毫秒最多一百多毫秒内,只要不是频繁发生,这点停顿是可以接受的。所以,Serial收集器对于运行在Client模式下的虚拟机来说是一个很好的选择。

2.ParNew收集器

ParNew收集器是Serial收集器的多线程版本,除了使用多条线程进行垃圾收集之外,其余行为包含Serial收集器可用的所有控制参数(例如:-XX:SurvivorRatio、-XX:PretenureSizeThreshold、-XX:HandlePromotionFailure等)、收集算法、Stop The World、对象分配规则、回收策略等都与Serial收集器完全一样。ParNew收集器的工作过程如下图:

file

3.Parallel Scavenge收集器

Parallel Scavenge收集器也是一个新生代收集器,它也是使用复制收集算法。其特点是与其它收集器的关注点不同,CMS等收集嘎嘎关注点是尽可能缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目的是达到一个可控制的吞吐量(Throughput)。所以吞量就是CPU用于运行用户代码的时候与CPU总消耗时间的比值,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收回时间)。
停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户的体验;而高吞吐量可以最高次第的利用CPU时间,尽快的完成程序任务,主要适合在后妈运算而不需要太多交互的任务。Parallel Scavenge收集器提供了两个参数用于精确控制吞吐量,分别是控制最大垃圾收集停顿时间的-XX:MaxGCPauseMillis及直接设置吞吐量大小的-XX:GCTimeRatio。Parallel Scavenge收集器还有一个参数-XX:UseAdaptiveSizePolicy,这是一个开关参数,当这个参数打开之后,就不需要手工指定新生代大小、Eden与Survivor区的比例,晋升老年代对象年龄等细节参数了。虚拟机会根据当前系统的运行情况收集性能监控信息,动态调用事这些参数以提供最合适的停顿时间或最大的吞吐量,这种调节方式称为GC自适应调用策略(GC Ergonomics)。

4.Serial Old收集器

Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器,使用”标记-整理“算法。这个收集器的主要意义也是被client模式下的虚拟机使用。如果在server模式下,它主要有两大用途:一个是在JDK1.5及之前的版本中与Parallel Scavenge收集器搭配使用,另外一个就是作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure的时候使用。Serial Old收集器的工作过程如下图:

file

5.Parallel Old收集器

Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和”标记-整理“算法。这个收集器是在JDK1.6中才开始提供的,在此之前,新生代的Parallel Scavenge收集器一直处于比较尴尬的状态。原因是如果新生代选择了Parallel Scavenge收集器,老年代除了Serial Old(PS MarkSweep)收集器之外别无选择。
Parallel Old收集器的工作过程如下图:

file

6.CMS收集器

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。目前很大一部分的Java应用都集中中互联网站或B/S系统的服务端上,这类应用尤其重视服务的响应速度,CMS收集器就非常符合这应用的需求。
CMS收集器是基于”标记-清除“算法实现的,它的运作过程相对前面几种收集器来说要复杂一点,整个过程分为4个步骤,包括:

  • 初始标记(CMS initial mark)
  • 并发标记(CMS concurrent mark)
  • 重新标记(CMS remark)
  • 并发清除(CMS concurrent sweep)

其中初始标记、重新标记这两个步骤仍然需要”Stop The World”。初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快,并发标记阶段就是进行GC Roots Tracing的过程,而重新标记阶段则是为了修正并发标记期间,因为用程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。
由于整个过程上耗时最长的并发标记与并发清除过程中,收集器线程可以与用户线程一起工作,所以总体上说,CMS收集器的内存回收过程是与用户线程一起并发地执行的。执行图如下:

file

CMS是一款优秀的收集器,但它有三个显著缺点:

  • CMS收集器对CPU资源非常敏感。
  • CMS收集器无法处理浮动垃圾(Floating Garbage),可能出现”Concurrent Mode Failure”失败而导致另一次Full GC的产生。
  • CMS收集器采用“标记-清除”算法,这样会导致出现大量内存碎片。

7.G1收集器

G1收集器是垃圾收集器理论进一步发展的产物,它与前面的CMS收集器相比有两个显著改进:一是G1收集器是基于“标记-整理”算法实现,也就是说它不会产生内存碎片,这对于长时间运行的应用系统来说非常重要。二是它可以非常精确地控制停顿,即能让使用都明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不超过N毫秒,这几乎已经是实时Java(RTSJ)的垃圾收集器的特征了。
G1收集器可以实现在基本不牺牲吞量的前提下完成低停顿的内存回收,这是由于它能够极力地避免全区域的垃圾收集,之前的收集器进行收集的范围都是整个新生代或老年代,而G1将Java堆(包含新生代与老年代)划分为多个大小固定的独立区域,并且跟踪这些区域里的垃圾堆积程度,在后台维护一个优先列表,每次根据允许的收集时间,优先回收垃圾最多的区域(这就是Garbage First名称的由来)。区域划分及优先级的区域回收,保证了G1收集器在有限时间内可以获得最高的收集效率。

玩转java虚拟机系列

深入理解java虚拟机系列

jdk命令行工具系列

jdk可视化工具系列

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注

关注我们