目前衡量垃圾收集器的三个最重要指标分别为:
这三个指标也被称为不可能的三角,即无法做到三者间的,随着硬件性能的提升,人们反而可以容忍内存占用的扩大,对延迟的容忍度反而降低。
该图展示了七种用于不同分代的分代收集器
Serial 收集器是一个新生代收集器,采用复制算法。Serial Old 收集器是 Serial 收集器的老年代版本,采用标记整理算法。
Serial 收集器与 Serial Old 收集器都是一个单线程收集器,在垃圾回收时,他们会暂停所有用户线程,直到垃圾收集的结束。一旦所需要回收的垃圾较多时,则用户线程等待的时间便会相当漫长, 从而影响用户体验。
Serial 收集器是 HotSpot 虚拟机运行在客户端下默认的新生代收集器,与其他收集器相比简单高效,并且内存消耗较少, 在单核或多核环境下,几乎没有线程切换的开销。
Serial Old 收集器主要也是供客户端模式下的 HotSpot 虚拟机使用。Serial Old 还可以在服务端作为 CMS 收集器发生 Concurrent Mode Failure 失败后的备份预案。
ParNew 收集器可以视为是 Serial 收集器的多线程版本。因为其所有控制参数、收集算法、Stop The World、对象分配规则、回收策略都与Serial 收集器完全一致。
ParNew 收集器在单核环境中的性能始终不如 Serial 收集器。
Parallel Scavenge 收集器也是一个新生代收集器,也是基于标记复制算法实现的垃圾收集器,也是能够并行收集的多线程收集器。如果 CMS 收集器关注点在与尽量缩短垃圾收集时的时间,Parallel Scavenge 收集器则的目标则是达到一个可控的吞吐量。
Parallel Old 收集器可以视为 Parallel Scavenge 收集器的老年代版本
CMS (Concurrent Mark Sweep) 收集器是一种以获取最短回收停顿时间为目标的收集器,通常用于 B/S 系统服务端上,对服务的响应速度较为关注。
CMS 收集器工作分为以下四个步骤
concurrent mode failure 由于 CMS是和用户线程并行的,如果在并行清理的过程中,老年代产生的垃圾不足以容纳应用产生的垃圾则会产生 concurrent mode failure
CMS 有三个明显的缺点
CMS 在解决“对象消失”问题时采用的是增量更新的方法
G1 是一款主要面向服务端的垃圾收集器,G1 相较于其他几个收集器,可以面向堆内任何部分来组成回收集,而不只是单单面对新生代(Minor GC),老年代(Major GC)或者整堆(Full GC)这三者中的一个。
G1 回收器的回收标准并不是判断对象是哪个代,而是那块内存中存放的垃圾数量最多,垃圾回收的收益就最大。
G1 开创了基于 Region 的堆内存布局。G1 不再坚持以固定的大小以及固定的数量来分代划分区域,而是把连续的 Java 堆划分成为多个大小不等的独立区域。每个区域可以根据需要来扮演 Eden、Survivor、或者老年代空间,这样无论是新创建的对象还是已存活了一段时间的对象还是 熬过多次收集的对象,都能取到很好的收集效果。
在 Region 中还有一个名为 Humongous 区域,专门用来存储大对象,只要是大小超过 Region 一半的对象就会被认定为大对象。
G1 收集器虽然仍然保留了新生代和老年代的概念,但是 新生代和老年代都不是固定的了,他们之间的内存区域并不是相连的,因此避免了在整个 Java 堆中进行全区域的垃圾收集。
G1 收集器也需要耗费相当于 Java堆中 10% ~ 20%的额外内存来维持收集器的工作。
G1 收集器在解决 “对象消失”问题时采用的是原始快照 (SATB)
算法。
G1 垃圾收集器为了解决在垃圾回收过程中新对象内存的分配问题时,G1 为每一个 Region 都设置了两个 TAMS (Top at Mark Start)
的指针。用于在 Region 上划分一部分空间用作新对象内存的分配,并发回收时新分配的对象地址都必须要在这两个,G1 回收器在垃圾回收时会默认这些对象时存活的。
G1 回收器回收的四个工作阶段:
Shenandoah 和 G1 一样采用基于 Region 的内存布局,同样有着存放大对象的 Humongous Region ,默认回收策略也是优先处理回收价值最大的 Region。
Shenandoah 与 G1 收集器最大的区别在与堆内存的管理上有三个明显的区别:
Shenandoah 收集器的工作过程可以分为以下 9 个阶段:
读写屏障和 “ Brooks Pointers”
在原有对象的前面加上一个新引用字段,在正常不处于并发移动的情况下,该引用对象指向自己。在对象移动完成后,指针则会直接指向新的地址上。这个指针便是 "Brooks Pointers"。
由于复制过程是和用户线程并发运行的,如果在运行过程中有用户线程想要对移动中的对象进行写操作,势必会造成一些问题。为了避免这种情况的发生,可以对指针的访问采取同步错失。即用户线程和收集器线程只能有其中一个对其进行访问。或者也可以采用 CAS 操作,而 Shenandoah 垃圾收集器采用的也是 CAS 操作。
如果在复制过程中进行单纯的读操作,则没有必要担心。因为无论指针指向哪一个,都不会影响读取的结果。
ZGC 是一款基于 Region 内存布局的,不设分代的,使用了读屏障、染色指针和内存多重映射技术来实现可并发的标记整理算法的低延时垃圾收集器。
ZGC 的 Region 也称为 Page 或 ZPage。
Page 的堆内存区域可以分为三种:
染色指针是一种直接将少量额外信息存储在指针上的技术。在 64 位系统中,理论可以访问的内存高达 2 64 2^{64} 264 个字节。但在实际上,由于硬件和操作系统的限制,可用于寻址的范围有限。
在 Linux 系统下 64 位指针的高 18 位不能用于寻址。但剩余 46 位指针所能支持的64TB 依旧能满足大部分服务器的需求。因此染色指针将 46 位指正的前4位取出来,用于存储 4 个标志位。
染色指针的优点:
PS: 染色指正不支持 32 位系统
ZGC 的运作过程
绿色为 Stop the World 的时间,红色为可并发运行的过程。由图,大家可以对 GC 的过程有一个直观的感受