JVM
4.1 哪些对象可以作为GC ROOT?
- 虚拟机栈(栈帧中的局部变量表)中引用的对象
- 本地方法栈中引用的对象
- 方法区静态变量引用的对象
- 方法区常量引用的对象
- 被同步锁持有的对象
- JNI(Java Native Interface)引用的对象
4.2 常用垃圾收集器
1、Serial 收集器
-
单线程收集器
-
收集时暂停用户线程(Stop the World)
-
简单高效(与其他收集器的单线程相比)
新生代 | 老年代 |
---|---|
标记-复制 | 标记-整理 |
2、ParNew收集器
-
Serial多线程版本
-
收集时暂停用户线程(Stop the World)
-
除了 Serial 收集器外,只有它能与 CMS 收集器(真正意义上的并发收集器,后面会介绍到)配合工作
新生代 | 老年代 |
---|---|
标记-复制 | 标记-整理 |
3、Parallel Scavenge收集器
- 类似ParNew,不过可以指定老年代使用串行还是并行收集
java
-XX:+UseParallelGC
使用 Parallel 收集器+ 老年代串行
-XX:+UseParallelOldGC
使用 Parallel 收集器+ 老年代并行
-
关注吞吐量(CMS关注用户体验)
-
收集时暂停用户线程(Stop the World)
-
JDK8默认收集器( JDK1.8 默认使用的是 Parallel Scavenge + Parallel Old,如果指定了-XX:+UseParallelGC 参数,则默认指定了-XX:+UseParallelOldGC,可以使用-XX:-UseParallelOldGC 来禁用该功能)
新生代 | 老年代 |
---|---|
标记-复制 | 标记-整理 |
4、Serial Old收集器
- Serial 老年代版本,单线程。
- 收集时暂停用户线程(Stop the World)
- 用于在Jdk1.5以上和Paraller Old搭配使用;或者作为CMS收集器的备选方案
新生代 | 老年代 |
---|---|
标记-复制 | 标记-整理 |
5、Paraller Old收集器
- Parallel Scavenge老年代版本
- 注重吞吐量
- 收集时暂停用户线程(Stop the World)
6、CMS(Concurrent Mark Sweep)收集器
- 是一种以获取最短回收停顿时间为目标的收集器,注重用户体验
- HotSpot第一款真正意义上的并发收集器,第一次实现让垃圾线程和用户线程同时工作 收集方式 标记-清除
收集流程:
- 初始标记:暂停所有其他线程,记录直接与root连接的对象,速度很快
- 并发标记:同时开启GC和用户线程,使用一个闭包结构去记录可达对象。但是在这个阶段结束后,这个闭包结构并不能保证当前所有对象可达,因为用户线程会不断的更新引用域,所以GC线程无法保证可达性分析的实时性。这个算法作用就是跟踪记录这些发生引用更新的地方。
- 重新标记:作用是修正并发标记时因为用户程序而导致标记变更的那一部分对象的标记记录,这个阶段的停顿时间比初始标记时间长,但是远远比并发标记时间短。
- 并发清除:开启用户线程后,同时GC线程开始对未标记的区域做清扫。
优点:
- 并发收集
- 停顿低,用户线程友好
缺点:
- 对CPU资源敏感
- 无法处理浮动垃圾
- 使用的标记-清除算法会导致收集结束时有大量空间碎片
JDK9开始,CMS已被弃用
7、G1收集器
- 面向服务器的垃圾收集器,极高概率满足GC停顿时间和吞吐量有要求
- 并行与并发:可以充分利用CPU、多核环境下的硬件优势,使用多个CPU(CPU或者核心)来缩短stop-the-world停顿时间。部分其他收集器原本需要停顿Java线程执行GC,但是G1仍然可以通过并发的方式让Java程序继续执行
- 分代收集:不需要和其他收集器配合,自己使用分代收集
- 空间整合:整体可以看做是基于"标记-整理"算法实现的收集器;从局部看是基于"标记-复制"算法实现的。
- 可预测的停顿:G1的一个大优势,G1除了追求低停顿外,还能建立可预测的时间模型,能让使用者明确指定在一个长度为 M 毫秒的时间片段内,消耗在垃圾收集上的时间不得超过 N 毫秒。
收集流程
- 初始标记
- 并发标记
- 最终标记
- 筛选回收
G1 收集器在后台维护了一个优先列表,每次根据允许的收集时间,优先选择回收价值最大的 Region(这也就是它的名字 Garbage-First 的由来) 。这种使用 Region 划分内存空间以及有优先级的区域回收方式,保证了 G1 收集器在有限时间内可以尽可能高的收集效率(把内存化整为零)。
从 JDK9 开始,G1 垃圾收集器成为了默认的垃圾收集器。
8、ZGC 收集器
与 CMS 中的 ParNew 和 G1 类似,ZGC 也采用标记-复制算法,不过 ZGC 对该算法做了重大改进。
ZGC 可以将暂停时间控制在几毫秒以内,且暂停时间不受堆内存大小的影响,出现 Stop The World 的情况会更少,但代价是牺牲了一些吞吐量。ZGC 最大支持 16TB 的堆内存。
ZGC 在 Java11 中引入,处于试验阶段。经过多个版本的迭代,不断的完善和修复问题,ZGC 在 Java15 已经可以正式使用了。
不过,默认的垃圾回收器依然是 G1。你可以通过下面的参数启用 ZGC:
java
java -XX:+UseZGC className
在 Java21 中,引入了分代 ZGC,暂停时间可以缩短到1毫秒以内。
你可以通过下面的参数启用分代 ZGC:
java
java -XX:+UseZGC -XX:+ZGenerational className
4.3各个收集器之间的关系
各个垃圾收集器可以配合使用