voiletbin 2020-06-14
如果说垃圾收集算法是内存回收的方法论,那垃圾收集器就是内存回收的实践者。本次要介绍的是几款“经典”的垃圾收集器,之所以被称之为“经典”,是为了与几款目前仍处于实验状态,但是执行效果上哟革命性改进的高性能低延迟收集器区分开来,虽然算不上最先进的技术,但却是在实践中千锤百炼,足够成熟,可以在商用生产环境上放心使用的全部垃圾收集器。
这些“经典”收集器之间的关系图
这七种作用于不同分代的收集器,如果两个之间存在连续,说明可以搭配使用。目前这些垃圾收集器并不都是“万能”的,所以针对于各个垃圾收集器,我们的目的是根据自己的具体场景而去选择合适的收集器。
Serial收集器是最基础、历史最悠久的收集器,曾经(在JDK1.3.1之前)是HotSpot虚拟机新生代收集器的唯一选择。这个收集器是一个单线程工作的收集器,在进行垃圾收集时,必须暂停其他所有工作现场,直到它收集结束。
Serial/Serial Old收集器的运行过程。
对于这个收集器,虽然是单线程并且需要在暂停其他所有工作线程。但这个收集器却并不是已经被抛弃或是过时的,因为它有着优于其他收集器的地方,那就是简单高效(与其他收集器的单线程相比)。对于内存资源受限的环境,它是所有收集器里额外内存消耗最小的;对于单核处理器或处理器核心数较少的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自如可以获得最高的线程收集效率。
ParNew收集器实质上Serial收集器的多线程并行版本,除了同时使用多条线程进行垃圾收集之外,其余的行为包括Serial收集器可用的所有控制参数、收集算法、Stop The World、对象分配规则、回收策略等都与Serial收集器完全一致,在实现上也是共用了相当多的代码。
ParNew收集器的工作过程示意图:
ParNew收集器除了支持多线程并行收集之外,其他与Serial收集器相比并没有太多创新之处。但是它有一个与功能、性能无关却很重要的优势,除了Serial收集器外,目前只有它能与CMS收集器配合工作。但是随着G1收集器的出现,从JKD9开始,官方已经不再推荐ParNew和CMS这种组合了,所以ParNew也就慢慢开始退出历史舞台了。
Parallel Scavenge收集器也是一款新生代收集器,它同样是基于标记-复制算法实现的收集器,也是能够并行收集的多线程收集器。这款收集器的关注点和其他收集器不同,其他收集器的关注点是尽可能的缩短用户线程停顿时间,而Parallel Scavenge收集器的目标则是到达一个可控制的 吞吐量。 所谓吞吐量就是处理器用于运行用户代码的时间与处理器总消耗时间的比值。
Parallel Scavenge收集器的运行示意图
Parallel Scavenge收集器提供了两个参数用于精确控制吞吐量,分别是控制最大拦击收集停顿时间的-XX:MaxGCPauseMilis参数以及直接这是吞吐量大小的-XX:GCTimeRatio参数。
-XX:MaxGCPauseMilis参数运行的值是一个大于0的毫秒数,收集器将尽量保证内存回收花费的时间不超过用户设定值。
-XX:GCTimeRatio参数的值则应当是一个大于0小于100的整数,也就是垃圾收集时间占总时间的比率,相当于吞吐量的倒数。
Parallel Scavenge收集器还有一个参数-XX:+UserAdaptiveSizePolicy值得我们关注。这是一个开关参数,在被激活之后就不需要人工置顶Eden去与Survivor区的比率、以及晋升老年代对象大小等细节参数了。虚拟机会根据运行情况自行调节。自适应调节策略也是Parallel Scavenge收集器区别于ParNew收集器的一个重要特性。
Serial Old收集器是Serial 收集器的老年代版本,也是一个单线程收集器,同样适用标记-整理算法。这个收集器的主要意义也是提供客户端模式下的HotSpot虚拟机使用。
Serial Old运行示意图:
如果在服务端模式下,Serial Old收集器可能有两种用途:一种是在JDK5以及之前的版本中与Parallel Scavengen收集器搭配使用,另外一种就是作为CMS收集器发送失败时的后备预案,在并发收集发生Concurrent Mode Failure 时使用。
Parallel Old是Parallel Scavenge收集器的老年代版本,支出多线程并发收集,基于标记-整理算实现。这个收集器是在JDK6时才提供的,之前新生代选择了Parallel Scavenge收集器,老年代只能选择Serial Old(PS MarkSweep)收集器。
直到Parallel Old 收集器出现后,“吞吐量优先”收集器终于有了笔记名副其实的搭配组合,在注重吞吐量或者处理器资源较为稀缺的场合,都可以优先考虑Parallel Scavenge加Parallel Old收集器这个组合。
Parallel Old收集器工作示意图:
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。
CMS是基于标记-清除算法实现的,但是整个运作过程相对于前面几种收集器来说要复杂一些,整个过程分四步骤:
1、初始标记(CMS initial mark)
2、并发标记(CMS concurrent mark)
3、重新标记(CMS remark)
4、并发清除(CMS concurrent sweep)
初始标记、重新标记这两个步骤仍然需要停止用户线程。
Garbage First(简称G1)收集器是垃圾收集器技术发展历史上的里程碑是的成果,它开创了收集器面向局部收集的设计思路和基于Region的内存布局形式。
G1是一款主要面向服务端应的垃圾收集器。HotSpot开发团队最初赋予它的期望是替换CMS收集器。从JDK9发布之日,G1宣告取代Parallel Scavenge 加Parallel Old组合,称为服务端模式下的默认的垃圾收集器。
G1开创了基于Region的堆内存布局是它能够实现可以由用户指定用户线程停顿时间的关键。虽然G1也遵循分代收集的设计理论,但却不再坚持固定大小以及固定数量的分代区域划分,而是把连续的Java堆划分为多个大小相等的独立区域(Region),每一个Region采用不同的策略去处理,这样无理是新创建的对象还是已经存活了一段时间、熬过多次收集的旧对象都能获取很好的收集效果。
G1收集器的分区示意图:
如果我们不去计算用户线程运行过程中的动作,G1收集器的运作过程大致可划分为以下四个步骤:
最后结合上节说的垃圾收集算法总结一下:
垃圾收集算法 | 垃圾收集器 | ||
---|---|---|---|
标记-清除 | CMS | ||
标记-复制 | Serial | ParNew | Parallel Scavenge |
标记-整理 | Serial Old | Parallel Old | G1 |