JVM学习总结(五)GC收集器

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

  1. Serial收集器

Serial收集器是最基本的、发展历史最悠久的收集器,曾经是(在JDK1.3之前)是虚拟机新生代收集的唯一选择,它是一个单线程的收集器。

缺点:

  1. 只会使用一个CPU或一条收集线程去完成垃圾收集工作
  2. 在进行垃圾收集时,必须暂停其他所有的工作线程(Stop The World),直到它收集结束。

优点:

  1. 作为虚拟机运行在Client模式下的默认新生代收集器,简单高效(与其他收集器的单线程相比),因为对于限定的单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率

  2. ParNew收集器

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

优点: 多线程收集 ParNew收集器与Serial收集器相比并没有太多的创新之处,但它却是许多运行在Server模式下的虚拟机中首选的新生代收集器,其中一个与性能无关但很重要的原因是,除了Serial收集器外,目前只有它能与CMS收集器配合工作。

  1. Parallel Scavenge收集器

Parallel Scavenge收集器是一个新生代收集器,也是使用复制算法,且是并行的多线程

Parallel Scavenge收集器的关注点与其他收集器不同,CMS等收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目的则是达到一个可控制的吞吐量(Throughput)

吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间),虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%

两个参数用于精确控制吞吐量

  • 控制最大垃圾收集停顿时间的-XX:MaxGCPauseMillis参数
  • 直接设置吞吐量大小的-XX:GCTimeRatio参数

XX:MaxGCPauseMillis参数允许的值是一个大于0的毫秒数,收集器将尽可能地保证内存回收花费的时间不超过设定值。

自适应策略是Parallel Scavenge收集器与ParNew收集器的一个重要区别

  1. Serial Old收集器

Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器,使用“标记-整理”算法,主要在于给Client模式下的虚拟机使用。

如果在Server模式下,那么还有两大用途:

  1. 在JDK1.5以及之前的版本中与Parallel Scavenge收集器搭配使用

  2. 作为CMS收集器的后备预案,在并发收集器发生 Concurrent Mode Failure

  3. Parallel Old收集器

是Parallel Scavenge 收集器的老年代办版本,使用多线程和“标记-整理”算法,在JDK1.6版本时才开始

  1. CMS收集器

CMS(Concurrent Mark Sweep)收集器一种以获得最短回收停顿时间为目标的收集器。

收集过程分为四个过程:

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

初始标记、重新标记这两个步骤仍需要STW

初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快,并发标记阶段就是进行GC Roots Tracing的过程,而重新标记阶段则是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍微长一些,但远比并发标记的时间短

缺点:

  1. CMS收集器对CPU资源非常敏感

  2. CMS收集器无法处理浮动垃圾

  3. CMS收集器基于的标记 清楚算法会产生大量的空间碎片

  4. G1收集器

G1(Garbage First)收集器是JDK1.7提供的一个新的面向服务端应用的垃圾收集器,其目标就是替换掉JDK1.5发布的CMS收集器。

优点:

  • 并发与并行:G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU(CPU或CPU核心)来缩短停顿(Stop The World)时间。

  • 分代收集:G1不需要与其他收集器配合就能独立管理整个GC堆,但他能够采用不同方式去处理新建对象和已经存活了一段时间、熬过多次GC的老年代对象以获取更好收集效果。

  • 空间整合:从整体来看是基于“标记-整理”算法实现,从局部(两个Region之间)来看是基于“复制”算法实现的,但是都意味着G1运行期间不会产生内存碎片空间,更健康,遇到大对象时,不会因为没有连续空间而进行下一次GC,甚至一次Full GC。

  • 可预测的停顿:降低停顿是G1和CMS共同关注点,但G1除了追求低停顿,还能建立可预测的停顿模型,可以明确地指定在一个长度为M的时间片内,消耗在垃圾收集的时间不超过N毫秒

  • 跨代特性:之前的收集器进行收集的范围都是整个新生代或老年代,而G1扩展到整个Java堆(包括新生代,老年代)。

那么是怎么实现的呢?

  • 如何实现新生代和老年代全范围收集:其实它的Java堆布局就不同于其余收集器,它将整个Java堆划分为多个大小相等的独立区域(Region),仍然保留新生代和老年代的概念,可是不是物理隔离的,都是一部分Region(不需要连续)的集合。
  • 如何建立可预测的停顿时间模型:是因为有了独立区域Region的存在,就避免在Java堆中进行全区域的垃圾收集,G1跟踪各个Region里面的垃圾堆积的价值大小(回收可以获得的空间大小和回收所需要的时间的经验值),后台维护一个优先队列,根据每次允许的收集时间,优先回收价值最大的Region(Garbage-First理念)。因此使用Region划分内存空间以及有优先级的区域回收方式,保证了有限时间获得尽可能高的收集效率。
  • 如何保证垃圾回收真的在Region区域进行而不会扩散到全局:由于Region并不是孤立的,一个Region的对象可以被整个Java堆的任意其余Region的对象所引用,在做可达性判定确定对象是否存活时,仍然会关联到Java堆的任意对象,G1中这种情况特别明显。而以前在别的分代收集里面,新生代规模要比老年代小许多,新生代收集也频繁得多,也会涉及到扫描新生代时也会扫描老年代的情况,相反亦然。解决:G1收集器Region之间的对象引用以及新生代和老年代之间的对象引用,虚拟机都是使用Remembered Set来避免全堆扫描。G1中每个Region都有一个与之对应的Remembered Set,虚拟机发现程序在对Reference类型的数据进行写操作时,会产生一个Write Barrier暂时中断写操作,检查Reference引用的对象是否处于不同的Region之中(分代的例子中就检查是否老年代对象引用了新生代的对象),如果是则通过CardTable把相关引用信息记录到被引用对象所属的Region的Remembered Set之中,当进行内存回收时,在GC根节点的枚举范围中加入Remembered Set即可避免全堆扫描。

忽略Remembered Set的维护,G1的运行步骤可简单描述为:

①.初始标记(Initial Marking)

②.并发标记(Concurrenr Marking)

③.最终标记(Final Marking)

④.筛选回收(Live Data Counting And Evacution)

1.初始标记:初始标记仅仅标记GC Roots能直接关联到的对象,并且修改TAMS(Next Top at Mark Start)的值,让下一阶段用户程序并发运行时,能在正确可用的Region中创建新的对象。这阶段需要停顿线程,不可并行执行,但是时间很短。

2.并发标记:此阶段是从GC Roots开始对堆中对象进行可达性分析,找出存活对象,此阶段时间较长可与用户程序并发执行。

3.最终标记:此阶段是为了修正在并发标记期间因为用户线程继续运行而导致标记产生变动的那一份标记记录,虚拟机将这段时间对象变化记录在线程Remembered Set Logs里面,最终标记阶段需要把Remembered Set Logs的数据合并到Remembered Set中,这段时间需要停顿线程,但是可并行执行。

4.筛选回收:对各个Region的回收价值和成本进行排序,根据用户期望的GC停顿时间来制定回收计划。

如果现有的垃圾收集器没有出现任何问题,没有任何理由去选择G1,如果应用追求低停顿,G1可选择,如果追求吞吐量,和Parallel Scavenge/Parallel Old组合相比G1并没有特别的优势。

猜你喜欢

转载自my.oschina.net/hensemlee/blog/1805830