文章目录
垃圾收集器
一、概述
1、垃圾回收器概述
垃圾收集器没有在规范中进行过多的规定,可以由不同的厂商、不同版本的 JVM 来实现。
由于 Java 的使用场景很多,移动端,服务器等。所以就需要针对不同的场景,提供不同的垃圾收集器,提高垃圾收集的性能。
没有什么垃圾收集器是完美的,我们只能根据具体使用场景来选择最合适的收集器。
2、垃圾收集器分类
按工作模式
分,可以分为独占式垃圾回收器
和并发式垃圾回收器
。
- 独占式:STW一旦运行,就停止应用程序中的所有用户线程,直到垃圾回收过程完全结束。
- 并发式:STW与应用程序线程交替工作,以尽可能减少应用程序的停顿时间。
按线程数
分,可以分为串行垃圾回收器
和并行垃圾回收器
。
- 串行:同一时间段内只有一个 CPU 用于执行垃圾回收操作
- 并行:并行收集可以运用多个 CPU 同时执行垃圾回收,因此提升了应用的吞吐量,不过并行回收仍然与串行回收一样,采用独占式,使用了“Stop-the-World”机制。
3、GC 的性能指标
- 吞吐量:运行用户代码的时间占总运行时间的比例(总运行时间 = 程序的运行时间 + 内存回收的时间)
- 暂停时间:执行垃圾收集时,程序的工作线程被暂停的时间。
- 内存占用:Java 堆区所占的内存大小。
吞吐量、暂停时间、内存占用 这三者共同构成一个“不可能三角”。现在标准:在最大吞吐量优先的情况下,降低停顿时间
吞吐量优先,意味着 STW 的总时间要尽可能短:0.2 + 0.2 = 0.4 s(但是每次STW的时间长)
暂停时间优先,意味着单次 STW 的时间尽可能短:0.1 + 0.1 + 0.1 + 0.1 + 0.1 = 0.5 s(但是存在线程切换,STW的总时间长)
4、垃圾回收器发展史
有了虚拟机,就一定需要收集垃圾的机制,这就是 Garbage Collection,对应的产品我们称为 Garbage Collector。
-
1999 年随 JDK1.3.1 一起来的是串行方式的 Serial GC,它是第一款 GC。ParNew GC是 Serial GC的多线程版本
-
2002 年 2 月 26 日,Parallel GC 和 CMS(Concurrent Mark Sweep) GC 跟随 JDK1.4.2 一起发布·
-
Parallel GC 在 JDK6 之后成为 HotSpot 默认 GC。
-
2012 年,在 JDK1.7u4 版本中,G1 可用。
-
2017 年,JDK9 中 G1 变成默认的垃圾收集器,以替代 CMS。
-
2018 年 3 月,JDK10 中 G1 垃圾回收器的并行完整垃圾回收,实现并行性来改善最坏情况下的延迟。
-
2018 年 9 月,JDK11 发布。引入 Epsilon 垃圾回收器,又被称为 "No-Op(无操作)“ 回收器。
同时,引入 ZGC:可伸缩的低延迟垃圾回收器(Experimental)
-
2019 年 3 月,JDK12 发布。增强 G1,自动返回未用堆内存给操作系统。
同时,引入 Shenandoah GC:低停顿时间的 GC(Experimental)。
-
2019 年 9 月,JDK13 发布。增强 ZGC,自动返回未用堆内存给操作系统。
-
2020 年 3 月,JDK14 发布。删除 CMS 垃圾回收器。扩展 ZGC 在 macos 和 Windows 上的应用
5、7 种经典的垃圾收集器
按照回收方式分:
- 串行回收器:Serial、Serial Old
- 并行回收器:ParNew、Parallel Scavenge、Parallel old
- 并发回收器:CMS、G1
按照分代关系分:
-
新生代收集器:Serial、ParNew、Parallel Scavenge
-
老年代收集器:Serial Old、Parallel Old、CMS
-
整堆收集器:G1
-
两个收集器间有连线,表明它们可以搭配使用
其中 Serial Old 作为 CMS 出现 “
Concurrent Mode Failure
” 的后备预案。 -
红色虚线
JDK 8 中:弃用 Serial+CMS、ParNew+Serial Old 这两个组合,并在 JDK9 中完全取消了这些组合的支持,即:移除。
-
绿色虚线
JDK14 中:弃用 Parallel Scavenge 和 Serialold GC 组合(JEP366)
-
JDK14 中:删除 CMS 垃圾回收器(JEP363)
6、查看默认的垃圾收集器
-XX:+PrintCommandLineFlags
:查看命令行相关参数(包含使用的垃圾收集器)
使用命令行指令:jinfo -flag 相关垃圾回收器参数 进程ID
二、Serial 收集器:串行回收
1、概述
- Serial 收集器是最基本、历史最悠久的垃圾收集器。JDK1.3 之前回收新生代唯一的选择。
- Serial 收集器是
HotSpot虚拟机
在Client模式
默认的年轻代
垃圾收集器 - Serial 收集器采用
复制算法
、串行回收
和stop-the-world机制
执行内存回收。 - Serial 收集器进行垃圾收集时,**必须暂停其他所有的工作线程,**直到它收集结束。
2、Serial Old 收集器
除了年轻代之外,Serial 收集器还提供用于执行老年代垃圾收集的 Serial Old 收集器。
- Serial Old 收集器是
Client模式
默认的老年代
垃圾回收器。 - Serial Old 收集器在
Server模式
主要有两个用途:- 与新生代的 Parallel Scavenge 收集器配合使用
- 作为老年代 CMS 收集器的后备垃圾收集方案
3、分代组合
- 年轻代:Serial 收集器。
串行回收
、复制算法
(HotSpot 中 Client 模式下 默认的 新生代 垃圾收集器) - 老年代:Serial Old 收集器。
串行回收
、标记-整理算法
(Client 模式下 默认的 老年代 垃圾收集器)
4、优缺点
优点:对于 Client 模式 单核CPU 的场景,由于没有线程交互的开销,可以获得最高的收集效率。简单而高效。
缺点:
- 只会使用 一个 CPU 或 一条收集线程 去完成垃圾收集工作。
- 进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束(Stop The World)
5、使用场景
Serial 收集器适用于:
-
Client 模式 单核CPU。
-
GC耗时较少,且不是很频繁的场景,使用串行回收器也是可以接受的。
例如用户的桌面应用,可用内存一般不大(一两百M),可以在较短时间内完成垃圾收集(一百多ms以内)
而对于交互较强的应用而言,就不适合使用串行垃圾收集器了。所以 Java Web 中是不会采用串行垃圾收集器的。
6、参数配置
在 HotSpot 虚拟机中,使用-XX:+UseSerialGC
参数可以指定年轻代和老年代都使用串行收集器。
等价于年轻代用 Serial 收集器,且老年代用 Serial Old 收集器
三、ParNew 收集器:并行回收
1、概述
- ParNew 收集器是 Serial 收集器的多线程版本。(Par:Parallel 的缩写,New:只能处理新生代)
- ParNew 收集器是 很多JVM 在
Server模式
默认的年轻代
垃圾收集器 - ParNew 收集器采用
复制算法
、并行回收
和stop-the-world机制
执行内存回收。 - 除 Serial 收集器外,目前只有 ParNew 收集器 能与 CMS 收集器配合工作。
2、分代组合
- 年轻代:ParNew 收集器,
并行回收
,复制算法
(很多 JVM 运行在 Server 模式下新生代的默认垃圾收集器) - 老年代:Serial Old 收集器,
串行回收
,标记-整理算法
(Client 模式下默认的老年代垃圾回收器)
- 对于年轻代,回收次数频繁,使用并行方式高效。
- 对于老年代,回收次数少,使用串行方式节省资源。(CPU 并行需要切换线程,串行可以省去切换线程的资源)
3、优缺点
优点:多核CPU的场景下,可以充分利用多 CPU、多核心等物理硬件资源优势,可以更快速地完成垃圾收集,提升程序的吞吐量。
缺点:单核CPU的情况下,ParNew 收集器并不比Serial 收集器更高效。
4、参数配置
-XX:+UseParNewGC
:手动指定 年轻代 使用 ParNew 收集器,不影响老年代。
-XX:ParallelGCThreads
:限制线程数量,默认开启和 CPU 数相同的线程数。
四、Parallel 收集器:吞吐量优先
1、概述
- 与 ParNew 收集器相同的是:
- Parallel Scavenge 收集器也采用
复制算法
、并行回收
和stop-the-world机制
执行内存回收。
- Parallel Scavenge 收集器也采用
- 与 ParNew 收集器不同的是:
- Parallel Scavenge 收集器的目标是达到一个
可控制的吞吐量
,吞吐量优先。 - Parallel Scavenge 收集器支持
自适应调节策略
。在这种
- Parallel Scavenge 收集器的目标是达到一个