分类与对比
垃圾回收器分类
当我们在讨论垃圾回收器时,往往也会涉及到很多的概念;譬如并行(Parallel)与并发(Concurrent
-
并行指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态;并发指用户线程与垃圾收集线程同时执行
( 但不一定是并行的,可能会交替执行) ,用户程序在继续运行,而垃圾收集程序运行于另一个CPU 上。 -
Minor GC 指发生在新生代的垃圾收集动作,因为Java 对象大多都具备朝生夕灭的特性,所以Minor GC 非常频繁,一般回收速度也比较快;Major GC 指发生在老年代的GC ,出现了Major GC ,经常会伴随至少一次的Minor GC( 但非绝对的,在Parallel Scavenge 收集器的收集策略里就有直接进行Major GC 的策略选择过程) ,Major GC 的速度一般会比Minor GC 慢10 倍以上。
从不同角度分析垃圾回收器,可以将其分为不同的类型:
-
线程数:分为串行垃圾回收器和并行垃圾回收器。
- 串行垃圾回收器一次只使用一个线程进行垃圾回收;
- 并行垃圾回收器一次将开启多个线程同时进行垃圾回收。在并行能力较强的
CPU 上,使用并行垃圾回收器可以缩短GC 的停顿时间;
-
工作模式:分为并发式垃圾回收器和独占式垃圾回收器。
- 并发式垃圾回收器与应用程序线程交替工作,以尽可能减少应用程序的停顿时间;
- 独占式垃圾回收器
(Stop the world) 一旦运行,就停止应用程序中的其他所有线程,直到垃圾回收过程完全结束;
-
碎片处理方式:分为压缩式垃圾回收器和非压缩式垃圾回收器。
- 压缩式垃圾回收器会在回收完成后,对存活对象进行压缩整理,消除回收后的碎片;
- 非压缩式的垃圾回收器不进行这步操作;
-
工作的内存区间:新生代垃圾回收器和老年代垃圾回收器。
垃圾回收器的衡量指标
我们最常用的评价垃圾回收器的指标就是吞吐量与停顿时间:
-
停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户的体验;
-
高吞吐量则可以最高效率地利用
CPU 时间,尽快地完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务;
具体的指标列举如下:
-
吞吐量:指在应用程序的生命周期内,应用程序所花费的时间和系统总运行时间的比值。系统总运行时间
= 应用程序耗时+GC 耗时。如果系统运行了100min ,GC 耗时1min ,那么系统的吞吐量就是(100-1)/100=99% ; -
垃圾回收器负载:和吞吐量相反,垃圾回收器负载指来记回收器耗时与系统运行总时间的比值;
-
停顿时间:指垃圾回收器正在运行时,应用程序的暂停时间。对于独占回收器而言,停顿时间可能会比较长。使用并发的回收器时,由于垃圾回收器和应用程序交替运行,程序的停顿时间会变短,但是,由于其效率很可能不如独占垃圾回收器,故系统的吞吐量可能会较低;
-
垃圾回收频率:指垃圾回收器多长时间会运行一次。一般来说,对于固定的应用而言,垃圾回收器的频率应该是越低越好。通常增大堆空间可以有效降低垃圾回收发生的频率,但是可能会增加回收产生的停顿时间;
-
反应时间:指当一个对象被称为垃圾后多长时间内,它所占据的内存空间会被释放;
-
堆分配:不同的垃圾回收器对堆内存的分配方式可能是不同的。一个良好的垃圾回收器应该有一个合理的堆内存区间划分;
在对象查找算法的帮助下我们可以找到内存可以被使用的,或者说那些内存是可以回收,更多的时候我们肯定愿意做更少的事情达到同样的目的。
JVM 垃圾回收器对比
名称 | 作用域 | 算法 | 特性 | 设置 |
---|---|---|---|---|
Serial | 二者皆采用了串行回收与 “Stop-the-World”, |
基于串行回收的垃圾回收器适用于大多数对于暂停时间要求不高的 |
使用 -XX:+UserSerialGC 手动指定使用 |
|
Throughput/Parallel | 并行回收和 “Stop-the-World”, |
程序吞吐量优先的应用场景中,在 |
使用 -XX:+UseParallelGC 手动指定使用 |
|
CMS,Concurrent-Mark-Sweep | 老年代垃圾回收器,又称作 Mostly-Concurrent 回收器 |
使用了标记清除算法,分为初始标记 |
并发低延迟,吞吐量较低。经过 |
使用 -XX:+UseConcMarkSweepGC 来手动指定使用 |
G1,Garbage First | 没有采用传统物理隔离的新生代和老年代的布局方式,仅仅以逻辑上划分为新生代和老年代,选择的将 |
使用了标记压缩算法 | 基于并行和并发、低延迟以及暂停时间更加可控的区域化分代式服务器类型的垃圾回收器 | 使用 -XX:UseG1GC 来手动指定使用 |
关于标记阶段有几个关键点是值得注意的:
- 开始进行标记前,需要先暂停应用线程,否则如果对象图一直在变化的话是无法真正去遍历它的。暂停应用线程以便
JVM 可以尽情地收拾家务的这种情况又被称之为安全点(Safe Point) ,这会触发一次Stop The World(STW) 暂停。触发安全点的原因有许多,但最常见的应该就是垃圾回收了。 - 暂停时间的长短并不取决于堆内对象的多少也不是堆的大小,而是存活对象的多少。因此,调高堆的大小并不会影响到标记阶段的时间长短。
当标记阶段完成后,
Serial GC
串行回收器主要有两个特点:第一,它仅仅使用单线程进行垃圾回收;第二,它独占式的垃圾回收。在串行回收器进行垃圾回收时,

Parallel GC
-XX:+UseParallelOldGC
可以在新生代和老生代都使用并行回收回收器,这是一对非常关注吞吐量的垃圾回收器组合,在对吞吐量敏感的系统中,可以考虑使用。参数 -XX:ParallelGCThreads
也可以用于设置垃圾回收时的线程数量。

CMS GC
CMS(Concurrent Mark-Sweep)是以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾回收器,适用于对停顿比较敏感,并且有相对较多存活时间较长的对象

- 初始标记
(STW initial mark) :在这个阶段,需要虚拟机停顿正在执行的任务,官方的叫法STW(Stop The Word) 。这个过程从垃圾回收的" 根对象" 开始,只扫描到能够和" 根对象" 直接关联的对象,并作标记。所以这个过程虽然暂停了整个JVM ,但是很快就完成了。 - 并发标记
(Concurrent marking) :这个阶段紧随初始标记阶段,在初始标记的基础上继续向下追溯标记。并发标记阶段,应用程序的线程和并发标记的线程并发执行,所以用户不会感受到停顿。 - 并发预清理
(Concurrent precleaning) :并发预清理阶段仍然是并发的。在这个阶段,虚拟机查找在执行并发标记阶段新进入老年代的对象( 可能会有一些对象从新生代晋升到老年代,或者有一些对象被分配到老年代) 。通过重新扫描,减少下一个阶段" 重新标记" 的工作,因为下一个阶段会Stop The World 。 - 重新标记
(STW remark) :这个阶段会暂停虚拟机,回收器线程扫描在CMS 堆中剩余的对象。扫描从" 跟对象" 开始向下追溯,并处理对象关联。 - 并发清理
(Concurrent sweeping) :清理垃圾对象,这个阶段回收器线程和应用程序线程并发执行。 - 并发重置
(Concurrent reset) :这个阶段,重置CMS 回收器的数据结构,等待下一次垃圾回收。
G1 GC
随着
总结而言,
- 并行性:
G1 在回收期间,可以有多个GC 线程同时工作,有效利用多核计算能力; - 并发性:
G1 拥有与应用程序交替执行的能力,部分工作可以和应用程序同时执行,因此,一般来说,不会在整个回收阶段发生完全阻塞应用程序的情况; - 分代
GC :G1 依然是一个分代回收器,但是和之前的各类回收器不同,它同时兼顾年轻代和老年代。对比其他回收器,或者工作在年轻代,或者工作在老年代; - 空间整理:
G1 在回收过程中,会进行适当的对象移动,不像CMS 只是简单地标记清理对象。在若干次GC 后,CMS 必须进行一次碎片整理。而G1 不同,它每次回收都会有效地复制对象,减少空间碎片,进而提升内部循环速度。 - 可预见性:为了缩短停顿时间,
G1 建立可预存停顿的模型,这样在用户设置的停顿时间范围内,G1 会选择适当的区域进行收集,确保停顿时间不超过用户指定时间。

- 初始标记
( 标记一下GC Roots 能直接关联的对象并修改TAMS 值,需要STW 但耗时很短) - 并发标记
( 从GC Root 从堆中对象进行可达性分析找存活的对象,耗时较长但可以与用户线程并发执行) - 最终标记
( 为了修正并发标记期间产生变动的那一部分标记记录,这一期间的变化记录在Remembered Set Log 里,然后合并到Remembered Set 里,该阶段需要STW 但是可并行执行) - 筛选回收
( 对各个Region 回收价值排序,根据用户期望的GC 停顿时间制定回收计划来回收) ;
ZGC
与标记对象的传统算法相比,