Serial收集器
Serial
收集器是最基础、历史最悠久的收集器,曾经(在JDK 1.3.1之前)是HotSpot
虚拟机新生代收集器的唯一选择。这个收集器是一个单线程工作的收集器,但它的“单线程”的意义并不仅仅是说明它只会使用一个处理器或一条收集线程去完成垃圾收集工作,更重要的是强调在它进行垃圾收集时,必须暂停其他所有工作线程,直到它收集结束。Stop The World这个词语也许听起来很酷,但这项工作是由虚拟机在后台自动发起和自动完成的,在用户不可知、不可控的情况下把用户的正常工作的线程全部停掉,这对很多应用来说都是不能接受的。
- 迄今为止,它依然是
HotSpot
虚拟机运行在客户端模式下的默认新生代收集器
优点:
- 简单而高效(与其他收集器的单线程相比),
- 对于内存资源受限的环境,它是所有收集器里额外内存消耗最小的
ParNew收集器
ParNew
收集器实质上是Serial
收集器的多线程并行版本,除了同时使用多条线程进行垃圾收集之外,其余的行为都与Serial
收集器完全一致,在实现上这两种收集器也共用了相当多的代码。
Parallel Scavenge收集器
Parallel Scavenge
收集器是一款新生代收集器,它同样是基于标记-复制算法实现的收集器,它的关注点与其他收集器不同,CMS
等收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge
收集器的目标则是达到一个可控制的吞吐
量(Throughput)。所谓吞吐量就是处理器用于运行用户代码的时间与处理器总消耗时间的比值
CMS收集器
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。目前很大一部分的Java应用集中在互联网网站或者基于浏览器的B/S系统的服务端上,这类应用通常都会较为关注服务的响应速度,希望系统停顿时间尽可能短,以给用户带来良好的交互体验。CMS收集器就非常符合这类应用的需求。
CMS收集器是基于标记-清除算法实现的,它的运作过程相对于前面几种收集器来说要更复杂一些,整个过程分为四个步骤,包括:
- 初始标记
- 并发标记
- 重新标记
- 并发清除
其中初始标记、重新标记这两个步骤仍然需要Stop The World
。
- 初始标记仅仅只是标记一下
GC Roots
能直接关联到的对象,速度很快 - 并发标记阶段就是从GC Roots的直接关联对象开始遍历整个对
象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行; - 新标记阶段则是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,停顿时间通常会比初始标记阶段稍长一些,但也远比并发标记阶段的时间短
- 并发清除阶段,清理删除掉标记阶段判断的已经死亡的
对象,由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的
缺点:
- CMS默认启动的回收线程数是(处理器核心数量
+3)/4,也就是说,如果处理器核心数在四个或以上,并发回收时垃圾收集线程只占用不超过25%的处理器运算资源,并且会随着处理器核心数量的增加而下降。但是当处理器核心数量不足四个时,
CMS对用户程序的影响就可能变得很大。 - 基于“标记-清除”算法,,会产生空间碎片
Serial Old收集器
Serial Old
是Serial
收集器的老年代版本,它同样是一个单线程收集器,使用标记-整理算法。这个收集器的主要意义也是供客户端模式下的HotSpot
虚拟机使用。
Parallel Old收集器
Parallel Old
是Parallel Scavenge
收集器的老年代版本,支持多线程并发收集,基于标记-整理算法实现。这个收集器是直到JDK 6时才开始提供的
Garbage First收集器
G1是一款主要面向服务端应用的垃圾收集器。HotSpot
开发团队最初赋予它的期望是(在比较长期的)未来可以替换掉JDK 5
中发布的CMS
收集器。
在G1收集器出现之前的所有收集器,垃圾收集的目标范围要么是整个新生代Minor GC
,要么就是整个老年代Major GC
,再要么就是整个Java堆Full GC
。而G1跳出了这个樊笼,它可以面向堆内存任何部分来组成进行回收,衡量标准不再是它属于哪个分代,而是哪块内存中存放的垃圾数量最多,回收收益最大,这就是G1收集器的Mixed GC
模式。
G1不再坚持固定大小以及固定数量的
分代区域划分,而是把连续的Java堆划分为多个大小相等的独立区域(Region),每一个Region
都可以根据需要,扮演新生代的Eden
空间、Survivor
空间,或者老年代
空间。收集器能够对扮演不同角色的Region采用不同的策略去处理.
-
Region
中还有一类特殊的Humongous
区域,专门用来存储大对象。G1认为只要大小超过了一个Region
容量一半的对象即可判定为大对象。每个Region的大小取值范围为1MB~32MB
- 对于那些超过了整个Region容量的超级大对象,将会被存放在N个连续的
Humongous Region
之中,G1的大多数行为都把Humongous Region
作为老年代的一部分来进行看待
G1收集器的运作过程大致可划分为以下四个步骤:
-
初始标记:仅仅只是标记一下
GC Roots
能直接关联到的对象,并且修改TAMS
指针的值,让下一阶段用户线程并发运行时,能正确地在可用的Region
中分配新对象。这个阶段需要停顿线程,但耗时很短,而且是借用进行Minor GC
的时候同步完成的,所以G1收集器在这个阶段实际并没有额外的停顿 -
并发标记:从
GC Root
开始对堆中对象进行可达性分析,递归扫描整个堆
里的对象图,找出要回收的对象,这阶段耗时较长,但可与用户程序并发执行。当对象图扫描完成以后,还要重新处理SATB
记录下的在并发时有引用变动的对象。 -
最终标记:对用户线程做另一个短暂的暂停,用于处理并发阶段结束后仍遗留下来的最后那少量的
SATB
记录。 -
筛选回收:负责更新
Region
的统计数据,对各个Region
的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,选择任意多个Region
构成回收集,然后把决定回收的那一部分Region
的存活对象复制到空的Region
中,再清理掉整个旧
Region
的全部空间。这里的操作涉及存活对象的移动,是必须暂停用户线程,由多条收集器线程并行完成的。
Shenandoah收集器
Shenandoah
作为第一款不由Oracle
(包括以前的Sun)公司的虚拟机团队所领导开发的HotSpot
垃圾收集器,Shenandoah
是一款只有OpenJDK
才会包含,而OracleJDK
里反而不存在的收集器(JDK12
)
最初Shenandoah
是由RedHat
公司独立发展的新型收集器项目,在2014年RedHat
把Shenandoah
贡献给了OpenJDK
,并推动它成为OpenJDK 12
的正式特性之一
Shenandoah
也是使用基于Region
的堆内存布局,同样有着用于存放大对象的Humongous Region
,默认的回收策略也同样是优先处理回收价值最大的Region
但在管理堆内存方面,它与G1至少有三个明显的不同之处:
- G1的回收阶段是可以多线程并行的,但却不能与用户线程并发
-
Shenandoah
(目前)是默认不使用分代收集的 -
Shenandoah
摒弃了在G1中耗费大量内存和计算资源去维护的记忆集,改用名为连接矩阵
的全局数据结构来记录跨Region
的引用关系
Shenandoah
收集器的工作过程大致可以划分为九个阶段,详细查看深入jvm虚拟机第三版
ZGC收集器
ZGC
(Z
并非什么专业名词的缩写,这款收集器的名字就叫作Z Garbage Collector
)是一款在
JDK 11
中新加入的具有实验性质的低延迟垃圾收集器,是由Oracle
公司研发的。2018年Oracle
将ZGC
提交给OpenJDK
,推动其进入OpenJDK 11
的发布清单之中。
ZGC
和Shenandoah
的目标是高度相似的,都希望在尽可能对吞吐量影响不太大的前提下,实现在任意堆内存大小下都可以把垃圾收集的停顿时间限制在十毫秒以内的低延迟。但是ZGC
和Shenandoah
的实现思路又是差异显著的
ZGC
收集器是一款基于Region
内存布局的,(暂时)
不设分代的,使用了读屏障、染色指针和内存多重映射等技术来实现可并发的标记-整理算法的,以低延迟为首要目标的一款垃圾收集器
Epsilon收集器
Epsilon
收集器在G1
、Shenandoah
或者ZGC
这些越来越复杂、越来越先进的垃圾收集器相继出现的同时,也有一个“反其道而行”的新垃圾收集器出现在JDK 11
的特征清单中——Epsilon
,这是一款以不能够进行垃圾收集为“卖点”的垃圾收集器,这种话听起来第一感觉就十分违反逻辑,这种“不干活”的收集器要它何用?
为了应对新的技术潮流,最近几个版本的JDK
逐渐加入了提前编译、面向应用的类数据共享等支持。Epsilon
也是有着类似的目标,如果应用只要运行数分钟甚至数秒,只要Java虚拟机能正确分配内存,在堆耗尽之前就会退出,那显然运行负载极小、没有任何回收行为的Epsilon
便是很恰当的选择。
总结
JDK 9
之前 Parallel Scavenge
加Parallel Old
是默认
,之后是G1